简体   繁体   English

c ++ 14中的泛型lambda:奇怪的行为

[英]Generic lambdas in c++14: weird behavior

maybe there's something I didn't fully get, however after reading "Use decltype on auto&& parameters to std::forward them" (from Effective Modern C++) I found something really weird. 也许有一些我没有完全得到的东西,但是在阅读“使用decltype on auto && parameters to std :: forward them”(来自Effective Modern C ++)后,我发现了一些非常奇怪的东西。

Let's suppose we have 我们假设有

int size(std::string &&s) { std::cout <<"std::string&&" <<std::endl; return s.size(); }
int size(const std::string &s) { std::cout <<"std::string const &" <<std::endl; return s.size(); }

I defined two lambas (one with auto, and one that correctly forward an auto&& param): 我定义了两个lambas(一个用auto,一个用于正确转发auto && param):

auto f = [](auto x) { return size(x); };

std::string s{ "buonanotte" };
std::cout <<f(s) <<std::endl;
std::cout <<f("fiorellino") <<std::endl;

auto g = [](auto&& x) { return size(std::forward<decltype(x)>(x)); };
std::cout <<g(s) <<std::endl;
std::cout <<g("fiorellino") <<std::endl;

Well, the results are the same: f(s) and g(s) call size(const std::string&), while f("fiorellino") and g("fiorellino") call the overload of size for rvalues. 好吧,结果是相同的:f(s)和g(s)调用大小(const std :: string&),而f(“fiorellino”)和g(“fiorellino”)调用rvalues的大小重载。

My question is: why is this happening? 我的问题是:为什么会发生这种情况? Shouldn't only the second lambda be able to distinguish between an lvalue and rvalue? 不仅第二个lambda能够区分左值和右值吗? I was expecting the first lambda (f()) calling twice size(const std::string&), but apparently this is not happening. 我期待第一个lambda(f())调用两次大小(const std :: string&),但显然这没有发生。

Am I doing something wrong? 难道我做错了什么?

Shouldn't only the second lambda be able to distinguish between an lvalue and rvalue? 不仅第二个lambda能够区分左值和右值吗? I was expecting the first lambda (f()) calling twice size(const std::string&) 我期待第一个lambda(f())调用两次大小(const std :: string&)

The string literal "fiorellino" is an lvalue, but when passing it to size , a temporary std::string object will be constructed from that string literal, and that temporary is an rvalue. 字符串文字"fiorellino"是一个左值,但是当它传递给size ,将从该字符串文字构造一个临时的std::string对象,该临时值是一个右值。

That is, the type of x in f for f("fiorellino") will be const char* . 也就是说, ff("fiorellino")x类型将是const char* That const char* is used to create a temporary std:string (an rvalue) when f calls size . f调用size时, const char*用于创建临时std:string (rvalue)。

This in turn leads to the rvalue overload being selected for f("fiorellino") based on this: "When used as a function argument and when two overloads of the function are available, one taking rvalue reference parameter and the other taking lvalue reference to const parameter, an rvalue binds to the rvalue reference overload" ( source ). 这反过来导致为f("fiorellino")选择rvalue过载,基于此: “当用作函数参数并且当函数的两个重载可用时,一个采用rvalue引用参数,另一个采用左值引用const参数,rvalue绑定到右值引用过载“ )。

OK, 好,

so I'd like to publish a full answer (Michael, again, thanks). 所以我想发表一个完整的答案(Michael,再次,谢谢)。 The thing I was getting confused was that "fiorellino" is an rvalue and a temporary is created for it and this temporary object, when a const T& and a T&& are available it will always bind to the T&& version. 令我感到困惑的是,“fiorellino”是一个右值,为它和这个临时对象创建了一个临时对象,当const T&和T &&可用时,它将始终绑定到T &&版本。 So far so good. 到现在为止还挺好。 This makes sense in effect (see http://en.cppreference.com/w/cpp/language/value_category where rvalues are described). 这实际上是有意义的(参见http://en.cppreference.com/w/cpp/language/value_category ,其中描述了rvalues)。

I have now changed my example in this way: 我现在用这种方式改变了我的例子:

int size(std::string &&s) { std::cout <<"std::string&&" <<std::endl; return s.size(); }
int size(const std::string &s) { std::cout <<"std::string const &" <<std::endl; return s.size(); }

std::string generate(int x) { return std::to_string(x); }

auto f = [](auto x) { return size(x); };

std::string s{ "buonanotte" };
std::cout <<"f(): "; f(s);
std::cout <<"f(): "; f("fiorellino");
std::cout <<"f(): "; f(generate(123));
std::cout <<"f(): "; f(std::move(s));

auto g = [](auto&& x) { return size(std::forward<decltype(x)>(x)); };

std::string s2{ "buonanotte" };
std::cout <<"g(): "; g(s2);
std::cout <<"g(): "; g("fiorellino");
std::cout <<"g(): "; g(generate(123));
std::cout <<"g(): "; g(std::move(s2));

Now generate(123) creates a correct temporary that will be wrongly forwarded by the first lambda, but is correctly forwarded by the second one. 现在,generate(123)创建了一个正确的临时,它将被第一个lambda错误地转发,但是被第二个lambda正确转发。

Look at the output: 看看输出:

f(): std::string const &
f(): std::string&&
f(): std::string const &
f(): std::string const &
g(): std::string const &
g(): std::string&&
g(): std::string&&
g(): std::string&&

even if I used std::move(s) (which creates an rvalue-reference) the first lambda always selects the const T& overload. 即使我使用std :: move(s)(创建一个rvalue-reference),第一个lambda总是选择const T&overload。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM