【问题标题】:Generic lambdas in c++14: weird behaviorc++14 中的通用 lambda:奇怪的行为
【发布时间】:2016-02-09 15:06:01
【问题描述】:

也许有些东西我没有完全理解,但是在阅读“使用 decltype on auto&& parameters to std::forward them”(来自 Effective Modern C++)之后,我发现了一些非常奇怪的东西。

假设我们有

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(); }

我定义了两个lambas(一个带有auto,一个正确转发auto&&参数):

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;

嗯,结果是一样的:f(s) 和 g(s) 调用 size(const std::string&),而 f("fiorellino") 和 g("fiorellino") 调用 size 的重载右值。

我的问题是:为什么会这样?不应该只有第二个 lambda 能够区分左值和右值吗? 我期待第一个 lambda (f()) 调用两次 size(const std::string&),但显然这没有发生。

我是不是做错了什么?

【问题讨论】:

  • 您能详细说明一下吗?我直接从 Scott Meyers 的书中举了这个例子。
  • 好吧,f("fiorellino") 不会导致构造一个临时的std::string(一个永远不会绑定到左值的)吗?所以我没有看到选择右值重载的问题。 "当用作函数参数并且函数的两个重载可用时,一个采用右值引用参数,另一个采用左值引用到 const 参数,一个右值绑定到右值引用重载"
  • @nwp :绝对错误。

标签: c++ lambda c++14


【解决方案1】:

难道不应该只有第二个 lambda 能够区分左值和右值吗?我期待第一个 lambda (f()) 调用两次 size(const std::string&)

字符串文字"fiorellino" 是一个左值,但是当将它传递给size 时,将从该字符串文字构造一个临时的std::string 对象,并且该临时对象是一个右值。

也就是说,ff("fiorellino")x 类型将是const char*const char* 用于在 f 调用 size 时创建一个临时的 std:string(右值)。

这反过来导致为f("fiorellino")选择右值重载,基于:“当用作函数参数并且函数的两个重载可用时,一个采用右值引用参数,另一个采用对 const 参数的左值引用,右值绑定到右值引用重载" (source)。

【讨论】:

  • 然而,x 是一个左值,但偶尔会被视为右值。
  • 好的,现在我明白了。我做了一个非常小的测试,添加了一个函数(希望)这次生成一个真正的右值:我添加了 std::string generate(int x) { return std::to_string(x);现在,当调用 f(generate(123)) 时,会调用左值重载,而当使用 g(generate(123)) 时,会调用正确的右值引用。非常感谢!
  • @Stadium 你能写一个答案吗?我还没明白。
【解决方案2】:

好的,

所以我想发布一个完整的答案(迈克尔,再次感谢)。 我感到困惑的是“fiorellino”是一个右值,并且为它和这个临时对象创建了一个临时对象,当一个 const T& 和一个 T&& 可用时,它将始终绑定到 T&& 版本。到目前为止,一切都很好。这实际上是有道理的(参见http://en.cppreference.com/w/cpp/language/value_category,其中描述了右值)。

我现在以这种方式更改了我的示例:

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));

现在 generate(123) 创建一个正确的临时变量,该临时变量将被第一个 lambda 错误地转发,但被第二个 lambda 正确转发。

看输出:

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&&

即使我使用了 std::move(s)(它创建了一个右值引用),第一个 lambda 总是选择 const T& 重载。

【讨论】:

  • ""fiorellino" is an rvalue" 注意字符串文字"fiorellino"是一个lvalue;由它构造的临时 std::string 对象是一个右值。
猜你喜欢
  • 1970-01-01
  • 2011-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-15
  • 2021-12-02
  • 1970-01-01
相关资源
最近更新 更多