字符串文字左值和右值引用的函数重载

时间:2019-09-27 10:53:03

标签: c++ language-lawyer overload-resolution value-categories

下面的函数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 版本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; }
      ^~~~

我的问题是:

  1. 哪个编译器正确?
  2. 使用Clang,为什么test(str1)test(str2)在左值重载时选择右值重载?
  3. 使用GCC,为什么呼叫test(str1)含糊?
  4. 这种情况是否有标准规则?
  5. 如何解决最后两个电话?

谢谢。

3 个答案:

答案 0 :(得分:13)

  
      
  1. 哪个编译器是正确的?
  2.   

GCC是正确的。

  
      
  1. 使用clang时,为什么str1和str2是左值时选择右值重载?
  2.   

test(str1);上的Clang错误,应该是模棱两可的。对于test(str2);str2可以隐式转换为指针,即数组到指针的衰减。转换后的char*是一个右值。由于与#3相同的原因,隐式转换序列具有相同的排名,因此首选非模板功能; test(char*&&)被选中。

  
      
  1. 使用gcc,为什么用str1进行的调用不明确?
  2.   

要调用test(const char (&)[1]),需要从char[1]const char[1]的资格转换;为了调用test(char*&&),需要数组到指针的转换。两者均符合exact match的资格,并且具有相同的排名。

  
      
  1. 这种情况是否有标准规则?
  2.   

请参见the ranking of implicit conversion sequences in overload resolutionimplicit conversions

  
      
  1. 如何解决最后两个电话?
  2.   

这取决于您的意图。

答案 1 :(得分:3)

字符串文字不是右值。 (

  
      
  1. 如何解决最后两个电话?
  2.   

您可以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]
  无效测试(const C(&)[N])[with C = char; long unsigned int N = 5]
  无效测试(C(&)[1])[with C = char]
  无效测试(C(&)[N])[with C = char; long unsigned int N = 5]
  无效测试(C(&&)[1])[with C = char]
  无效测试(C(&&)[N])[with C = char; long unsigned int N = 5]
  无效测试(const C(&&)[1])[with C = char]
  无效测试(const C(&&)[N])[with C = char; long unsigned int N = 5]

答案 2 :(得分:1)

感谢@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