使用左值调用时不使用通用引用参数的函数

时间:2015-12-28 16:30:41

标签: c++11 rvalue-reference

考虑这个简单的代码片段:

static void Foo(std::string&& arg) {
  printf("(universal reference) = %s\n", arg.c_str());
}

static void Foo(const std::string&& arg) {
  printf("(const universal reference) = %s\n", arg.c_str());
}

static void Foo(std::string& arg) {
  printf("(reference) = %s\n", arg.c_str());
}

static void Foo(const std::string& arg) {
  printf("(const reference) = %s\n", arg.c_str());
}

int main(int argc, const char * argv[]) {
  std::string value{"value"};
  const std::string const_value{"const_value"};

  Foo(value);
  Foo(const_value);
  Foo(std::string("temporary"));
  Foo("litteral");
}

Clang下的结果输出是:

(reference) = value
(const reference) = const_value
(universal reference) = temporary
(universal reference) = literal

为什么value不使用函数的通用引用版本?我认为普遍引用的一个主要好处是它们可以同时接受左值和右值?

PS:也不可能强迫它:

static void Foo(std::string&& arg) {
  printf("(universal reference) = %s\n", arg.c_str());
}

//static void Foo(const std::string&& arg) {
//  printf("(const universal reference) = %s\n", arg.c_str());
//}
//
//static void Foo(std::string& arg) {
//  printf("(reference) = %s\n", arg.c_str());
//}
//
//static void Foo(const std::string& arg) {
//  printf("(const reference) = %s\n", arg.c_str());
//}

int main(int argc, const char * argv[]) {
  std::string value{"value"};
  const std::string const_value{"const_value"};

  Foo(value);   <--- FAILS COMPILING: No matching function for call to 'Foo'
//  Foo(const_value);
  Foo(std::string("temporary"));
  Foo("literal");
}

更新:看来“通用引用”仅适用于模板,而不是常规功能,这就解释了为什么上述内容无法正常工作。

但是这是一个使用模板化函数的版本:

template<typename T>
static void Foo(T&& arg) {
  printf("%s\n", arg.c_str());
}

int main(int argc, const char * argv[]) {
  std::string value{"value"};
  const std::string const_value{"const_value"};

  Foo<std::string>(value);   <--- FAILS COMPILING: No matching function for call to 'Foo'
  Foo<std::string>(const_value);   <--- FAILS COMPILING: No matching function for call to 'Foo'
  Foo<std::string>(std::string("temporary"));
  Foo<std::string>("literal");
}

为什么value案例仍未通过通用引用(我明白为什么const_value不是这样)?

更新:供参考,这是适用于左值和右值的最终版本:

template<typename T>
static void Foo(T&& arg) {
  printf("%s\n", arg.c_str());
}

int main(int argc, const char * argv[]) {
  std::string value{"value"};
  const std::string const_value{"const_value"};

  Foo(value);
  Foo(const_value);
  Foo(std::string("temporary"));
  //Foo("literal");  <--- Cannot work anyway since template is instantiated with arg as a const char*
}

1 个答案:

答案 0 :(得分:2)

那些不是&#34;普遍参考&#34;。这些只是左值参考。

A&#34; universal reference&#34; (仅供参考:此术语不受欢迎)明确指出在使用模板参数推导时,直接应用于完全模板推导类型的rvalue引用(与vector<T> &&v相反,仅部分推导)。您的所有函数都不是模板函数,因此它们的行为类似于常规的右值引用。

foo(value)调用左值引用版本,因为作为参数传递的变量绑定到左值引用参数类型。

  

然而,这是一个使用模板化函数的版本:

这些函数都不使用模板参数推导。您直接明确指定模板参数,不允许通过您调用它的参数推断出它。

或更重要的是:

Foo<std::string>(value);
Foo(value);
Foo<std::string>(std::string("temporary"));
Foo(std::string("temporary"));

在第一种情况下,Tstd::string,因为您指定它是Foo<std::string>。因此,std::string&&需要valueT无法绑定到右值引用,因此它是编译错误。

在第二种情况下,value是基于参数表达式通过模板参数推导推导出的。 T是左值,因此std::string&推断为Foo。因此,在这种情况下,std::string& &&需要std::string&,其范围可降至T

在第三种情况下,std::stringFoo<std::string>。因此,std::string&&再次T。临时可以很好地绑定到右值参考,所以它就这样做了。

在第四种情况下,std::string是基于参数表达式通过模板参数推导推导出的。表达式是T临时prvalue,因此std::string推导为Foo(请注意缺少引用)。因此,std::string&&在这种情况下需要{{1}}。