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

Function with universal reference argument not being used when called with lvalue

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

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

UPDATE:看来 "universal references" 仅适用于模板,而不适用于常规函数,这解释了为什么上述内容无法正常工作。

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

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*
}

那些不是 "universal references"。这些只是右值引用。

A“universal reference”(仅供参考:这个术语已经失宠)明确指的是直接应用于完全模板推导类型的右值引用(与 vector<T> &&v 之类的东西相反)这只是部分推导)在使用模板参数推导时。 None 您的函数是模板函数,因此它们的行为类似于常规右值引用。

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


However here's a version using a templated function:

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

或者更重要的是:

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

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

第二种情况,T是根据参数表达式通过模板参数推导推导出来的。 value 是一个左值,所以 T 被推导为 std::string&。因此,在这种情况下,Foo 需要一个 std::string& &&,它会下降到 std::string&

第三种情况,Tstd::string。因此,Foo<std::string> 再次采用 std::string&&。临时对象可以很好地绑定到右值引用,所以它会这样做。

在第四种情况下,T是根据参数表达式通过模板参数推导推导出来的。该表达式是一个 std::string 临时纯右值,因此 T 被推导为 std::string(注意缺少引用)。所以 Foo 在这种情况下采用 std::string&&.