C++0x 右值引用和临时对象

标签 c++ c++11 temporary rvalue-reference

(我在 comp.std.c++ 上问了这个问题的一个变体,但没有得到答案。)

为什么这段代码中对f(arg)的调用会调用f的const ref重载?

void f(const std::string &); //less efficient
void f(std::string &&); //more efficient

void g(const char * arg)
{
     f(arg);
}

我的直觉说应该选择 f(string &&) 重载,因为 arg 无论如何都需要转换为临时的,而临时匹配右值引用比左值引用更好。

这不是在 GCC 和 MSVC 中发生的(编辑:感谢 Sumant:它不会在 GCC 4.3-4.5 中发生)。至少在 G++ 和 MSVC 中,任何左值都不会绑定(bind)到右值引用参数,即使创建了一个中间临时值。实际上,如果不存在 const ref 重载,编译器会诊断出错误。但是,编写 f(arg + 0)f(std::string(arg)) 确实 会像您一样选择右值引用重载期待。

从我对 C++0x 标准的阅读来看,在考虑 f(string &&) 是否可行时,似乎应该考虑将 const char * 隐式转换为字符串,只是就像传递 const lvalue ref 参数时一样。第 13.3 节(重载解决方案)在太多地方没有区分右值引用和 const 引用。此外,如果存在中间临时对象,阻止左值绑定(bind)到右值引用 (13.3.3.1.4/3) 的规则似乎不适用 - 毕竟,从临时对象移出是完全安全的。

这是:

  1. 我误读/误解了标准,实现的行为是预期的行为,我的示例应该按照它的方式运行是有充分理由的?
  2. 编译器供应商都犯了一个错误?还是基于常见实现策略的错误?或者错误,例如GCC(这个左值/右值引用绑定(bind)规则最早实现的地方),被其他供应商复制了吗?
  3. 标准中的缺陷、意外后果或应澄清的内容?

编辑:我有一个相关的后续问题:C++0x rvalue references - lvalues-rvalue binding

最佳答案

根据 FCD,GCC 做错了。 FCD 在 8.5.3 中提到了引用绑定(bind)

  • 如果引用是左值引用并且初始化表达式是[左值/类类型] ...
  • 否则,引用应为对非 volatile const 类型的左值引用(即 cv1 应为 const),或者引用应为右值引用且初始化表达式应为右值或具有函数类型。

您调用 std::string && 的情况与它们都不匹配,因为初始值设定项是 lvalue。它无法创建临时右值,因为该顶级项目符号已经需要一个右值。

现在,重载解析不直接使用引用绑定(bind)来查看是否存在隐式转换序列。相反,它在 13.3.3.1.4/2

When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the underlying type of the reference according to 13.3.3.1.

因此,重载决议会找出赢家,即使该赢家实际上可能无法绑定(bind)到该参数。例如:

struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };

void f(int&);
void f(B);
int main() { A a; f(a.bits); }

8.5 中的引用绑定(bind)禁止位域绑定(bind)到左值引用。但是重载决议表示转换序列是转换为 int 的序列,因此即使稍后进行调用,调用也是格式错误的,也会成功。因此,我的位域示例格式不正确。如果是选择 B 版本,它会成功,但需要用户定义的转换。

但是,该规则有两个异常(exception)。这些是

Except for an implicit object parameter, for which see 13.3.1, a standard conversion sequence cannot be formed if it requires binding an lvalue reference to non-const to an rvalue or binding an rvalue reference to an lvalue.

因此,以下调用是有效的:

struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };

void f(int&); /* binding an lvalue ref to non-const to rvalue! */
void f(B);
int main() { A a; f(1); }

因此,您的示例调用 const T& 版本

void f(const std::string &);
void f(std::string &&); // would bind to lvalue!

void g(const char * arg) { f(arg); }

但是,如果你说 f(arg + 0),你会创建一个右值,因此第二个函数是可行的。

关于C++0x 右值引用和临时对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2748866/

相关文章:

c++ - 在 C++ 中返回一个数组

C++ static_cast 和虚方法功能

c++ 11以下lambda函数的作用域是什么

Eclipse Indexer 中的 C++11 设置与 git 冲突

c++ - 使用临时对象而不将其存储在变量中

c++ - 程序因为字符串崩溃?

c++ - 如何从C++中的文本文件中获取特定单词?

c++ - 将重载函数(如 std::stoll)打包到 std::function

file - 如何使用公共(public) Gradle API 创建临时文件/文件夹?

c++ - 返回复制的对象