字符串文字左值和右值引用的函数重载
Function overload for string literals lvalue and rvalue reference
下面的函数test
针对左值空字符串、左值非空字符串和右值字符串进行了重载。我尝试使用 Clang 和 GCC 进行编译,但在这两种情况下我都没有得到预期的结果。
#include <iostream>
void test(const char (&)[1]){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
template <unsigned long int N>
void test(const char (&)[N]){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
void test(char*&&){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
int main(){
char str1[] = "";
char str2[] = "test";
test("");
test("test");
test(str1);
test(str2);
}
用 clang 输出 version 6.0.0-1ubuntu2:
clang++ test.cpp -o test.out && ./test.out
void test(const char (&)[1])
void test(const char (&)[N]) [N = 5]
void test(char *&&)
void test(char *&&)
使用 g++ 输出 (MinGW.org GCC-8.2.0-3):
g++ test.cpp -o test.exe && test.exe
test.cpp: In function 'int main()':
test.cpp:15:11: error: call of overloaded 'test(char [1])' is ambiguous
test(str1);
^
test.cpp:3:6: note: candidate: 'void test(const char (&)[1])'
void test(const char (&)[1]){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
^~~~
test.cpp:6:6: note: candidate: 'void test(const char (&)[N]) [with long unsigned int N = 1]'
void test(const char (&)[N]){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
^~~~
test.cpp:8:6: note: candidate: 'void test(char*&&)'
void test(char*&&){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
^~~~
我的问题是:
- 哪个编译器是正确的?
- 对于 Clang,为什么
test(str1)
和 test(str2)
在它们是左值时选择右值重载?
- 使用 GCC,为什么调用
test(str1)
不明确?
- 这种情况有标准规定吗?
- 如何解决最后两次调用?
谢谢。
字符串文字不是右值。 (→)
- How to fix the two last calls?
您可以 disambiguate everything 使用模板专业化:
#include <iostream>
template<typename C, std::size_t N>
void test(const C (&)[N]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C>
void test(const C (&)[1]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C, std::size_t N>
void test(const C (&&)[N]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C>
void test(const C (&&)[1]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C, std::size_t N>
void test(C (&)[N]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C>
void test(C (&)[1]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C, std::size_t N>
void test(C (&&)[N]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C>
void test(C (&&)[1]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
int main(){
char str1[] = "";
char str2[] = "test";
test("");
test("test");
test(str1);
test(str2);
test(std::move(str1));
test(std::move(str2));
const char str3[] = "";
const char str4[] = "test";
test(std::move(str3));
test(std::move(str4));
}
给予
void test(const C (&)[1]) [with C = char]
void test(const C (&)[N]) [with C = char; long unsigned int N = 5]
void test(C (&)[1]) [with C = char]
void test(C (&)[N]) [with C = char; long unsigned int N = 5]
void test(C (&&)[1]) [with C = char]
void test(C (&&)[N]) [with C = char; long unsigned int N = 5]
void test(const C (&&)[1]) [with C = char]
void test(const C (&&)[N]) [with C = char; long unsigned int N = 5]
- Which compiler is correct ?
GCC 正确。
- With clang, why str1 and str2 choose the rvalue overload while they are lvalues ?
Clang在test(str1);
上是错误的,应该是有歧义的。对于 test(str2);
,str2
可以隐式转换为指针,即数组到指针的衰减。转换后的 char*
是一个右值。同#3的原因,隐式转换序列具有相同的排序,则首选非模板函数; test(char*&&)
已选中。
- With gcc, why call with str1 is ambiguous ?
要调用test(const char (&)[1])
,需要从char[1]
到const char[1]
进行资格转换;要调用 test(char*&&)
,需要进行数组到指针的转换。两者都符合exact match的资格,并且排名相同。
- Is there a standard rule for this situation ?
参见 the ranking of implicit conversion sequences in overload resolution, and implicit conversions。
- How to fix the two last calls ?
这取决于您的意图。
谢谢@songyuanyao 的回答,明白后两种情况为什么选择test(char*&&)
了。由于@Darklighter 的回答,我能够在第一次重载时通过模板专业化消除歧义。
所以我解决了如下问题:
#include <iostream>
template <unsigned long int N>
void test(const char (&)[N]){
std::cout << __PRETTY_FUNCTION__ << " //non-empty literal" << std::endl;
}
template <>
void test(const char (&)[1]){
std::cout << __PRETTY_FUNCTION__ << " //empty literal" << std::endl;
}
void test(char*&&){
std::cout << __PRETTY_FUNCTION__ << " //string variable" << std::endl;
}
int main(){
char str1[] = "";
char str2[] = "test";
test("");
test("test");
test(str1);
test(str2);
}
输出:
clang++ test.cpp -o test.out && ./test.out
void test(const char (&)[1]) //empty literal
void test(const char (&)[N]) [N = 5] //non-empty literal
void test(char *&&) //string variable
void test(char *&&) //string variable
g++ test.cpp -o test.exe && test.exe
void test(const char (&)[N]) [with long unsigned int N = 1] //empty literal
void test(const char (&)[N]) [with long unsigned int N = 5] //non-empty literal
void test(char*&&) //string variable
void test(char*&&) //string variable
下面的函数test
针对左值空字符串、左值非空字符串和右值字符串进行了重载。我尝试使用 Clang 和 GCC 进行编译,但在这两种情况下我都没有得到预期的结果。
#include <iostream>
void test(const char (&)[1]){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
template <unsigned long int N>
void test(const char (&)[N]){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
void test(char*&&){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
int main(){
char str1[] = "";
char str2[] = "test";
test("");
test("test");
test(str1);
test(str2);
}
用 clang 输出 version 6.0.0-1ubuntu2:
clang++ test.cpp -o test.out && ./test.out
void test(const char (&)[1])
void test(const char (&)[N]) [N = 5]
void test(char *&&)
void test(char *&&)
使用 g++ 输出 (MinGW.org GCC-8.2.0-3):
g++ test.cpp -o test.exe && test.exe
test.cpp: In function 'int main()':
test.cpp:15:11: error: call of overloaded 'test(char [1])' is ambiguous
test(str1);
^
test.cpp:3:6: note: candidate: 'void test(const char (&)[1])'
void test(const char (&)[1]){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
^~~~
test.cpp:6:6: note: candidate: 'void test(const char (&)[N]) [with long unsigned int N = 1]'
void test(const char (&)[N]){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
^~~~
test.cpp:8:6: note: candidate: 'void test(char*&&)'
void test(char*&&){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
^~~~
我的问题是:
- 哪个编译器是正确的?
- 对于 Clang,为什么
test(str1)
和test(str2)
在它们是左值时选择右值重载? - 使用 GCC,为什么调用
test(str1)
不明确? - 这种情况有标准规定吗?
- 如何解决最后两次调用?
谢谢。
字符串文字不是右值。 (→)
- How to fix the two last calls?
您可以 disambiguate everything 使用模板专业化:
#include <iostream>
template<typename C, std::size_t N>
void test(const C (&)[N]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C>
void test(const C (&)[1]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C, std::size_t N>
void test(const C (&&)[N]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C>
void test(const C (&&)[1]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C, std::size_t N>
void test(C (&)[N]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C>
void test(C (&)[1]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C, std::size_t N>
void test(C (&&)[N]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template<typename C>
void test(C (&&)[1]) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
int main(){
char str1[] = "";
char str2[] = "test";
test("");
test("test");
test(str1);
test(str2);
test(std::move(str1));
test(std::move(str2));
const char str3[] = "";
const char str4[] = "test";
test(std::move(str3));
test(std::move(str4));
}
给予
void test(const C (&)[1]) [with C = char]
void test(const C (&)[N]) [with C = char; long unsigned int N = 5]
void test(C (&)[1]) [with C = char]
void test(C (&)[N]) [with C = char; long unsigned int N = 5]
void test(C (&&)[1]) [with C = char]
void test(C (&&)[N]) [with C = char; long unsigned int N = 5]
void test(const C (&&)[1]) [with C = char]
void test(const C (&&)[N]) [with C = char; long unsigned int N = 5]
- Which compiler is correct ?
GCC 正确。
- With clang, why str1 and str2 choose the rvalue overload while they are lvalues ?
Clang在test(str1);
上是错误的,应该是有歧义的。对于 test(str2);
,str2
可以隐式转换为指针,即数组到指针的衰减。转换后的 char*
是一个右值。同#3的原因,隐式转换序列具有相同的排序,则首选非模板函数; test(char*&&)
已选中。
- With gcc, why call with str1 is ambiguous ?
要调用test(const char (&)[1])
,需要从char[1]
到const char[1]
进行资格转换;要调用 test(char*&&)
,需要进行数组到指针的转换。两者都符合exact match的资格,并且排名相同。
- Is there a standard rule for this situation ?
参见 the ranking of implicit conversion sequences in overload resolution, and implicit conversions。
- How to fix the two last calls ?
这取决于您的意图。
谢谢@songyuanyao 的回答,明白后两种情况为什么选择test(char*&&)
了。由于@Darklighter 的回答,我能够在第一次重载时通过模板专业化消除歧义。
所以我解决了如下问题:
#include <iostream>
template <unsigned long int N>
void test(const char (&)[N]){
std::cout << __PRETTY_FUNCTION__ << " //non-empty literal" << std::endl;
}
template <>
void test(const char (&)[1]){
std::cout << __PRETTY_FUNCTION__ << " //empty literal" << std::endl;
}
void test(char*&&){
std::cout << __PRETTY_FUNCTION__ << " //string variable" << std::endl;
}
int main(){
char str1[] = "";
char str2[] = "test";
test("");
test("test");
test(str1);
test(str2);
}
输出:
clang++ test.cpp -o test.out && ./test.out
void test(const char (&)[1]) //empty literal
void test(const char (&)[N]) [N = 5] //non-empty literal
void test(char *&&) //string variable
void test(char *&&) //string variable
g++ test.cpp -o test.exe && test.exe
void test(const char (&)[N]) [with long unsigned int N = 1] //empty literal
void test(const char (&)[N]) [with long unsigned int N = 5] //non-empty literal
void test(char*&&) //string variable
void test(char*&&) //string variable