使用左值调用时不使用具有通用引用参数的函数
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"));
在第一种情况下,T
是 std::string
,因为您指定它是这样的。因此,Foo<std::string>
取一个std::string&&
。 value
无法绑定到右值引用,因此这是一个编译错误。
第二种情况,T
是根据参数表达式通过模板参数推导推导出来的。 value
是一个左值,所以 T
被推导为 std::string&
。因此,在这种情况下,Foo
需要一个 std::string& &&
,它会下降到 std::string&
。
第三种情况,T
为std::string
。因此,Foo<std::string>
再次采用 std::string&&
。临时对象可以很好地绑定到右值引用,所以它会这样做。
在第四种情况下,T
是根据参数表达式通过模板参数推导推导出来的。该表达式是一个 std::string
临时纯右值,因此 T
被推导为 std::string
(注意缺少引用)。所以 Foo
在这种情况下采用 std::string&&
.
考虑这个简单的代码片段:
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"));
在第一种情况下,T
是 std::string
,因为您指定它是这样的。因此,Foo<std::string>
取一个std::string&&
。 value
无法绑定到右值引用,因此这是一个编译错误。
第二种情况,T
是根据参数表达式通过模板参数推导推导出来的。 value
是一个左值,所以 T
被推导为 std::string&
。因此,在这种情况下,Foo
需要一个 std::string& &&
,它会下降到 std::string&
。
第三种情况,T
为std::string
。因此,Foo<std::string>
再次采用 std::string&&
。临时对象可以很好地绑定到右值引用,所以它会这样做。
在第四种情况下,T
是根据参数表达式通过模板参数推导推导出来的。该表达式是一个 std::string
临时纯右值,因此 T
被推导为 std::string
(注意缺少引用)。所以 Foo
在这种情况下采用 std::string&&
.