【问题标题】:Function overload for string literals lvalue and rvalue reference字符串文字左值和右值引用的函数重载
【发布时间】:2019-09-27 10:53:03
【问题描述】:

下面的函数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. 如何修复最后两个调用?

谢谢。

【问题讨论】:

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


    【解决方案1】:
    1. 哪个编译器是正确的?

    GCC 是正确的。

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

    test(str1); 上的 Clang 错误,应该是模棱两可的。对于test(str2);str2 可以隐式转换为指针,即数组到指针的衰减。转换后的 char* 是一个右值。和#3一样的原因,隐式转换序列具有相同的排名,那么优先使用非模板函数; test(char*&amp;&amp;) 被选中。

    1. 使用 gcc,为什么用 str1 调用是模棱两可的?

    要调用test(const char (&amp;)[1]),需要从char[1]const char[1]的资格转换;要调用test(char*&amp;&amp;),需要进行数组到指针的转换。两者都符合exact match 且排名相同。

    1. 这种情况有标准规则吗?

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

    1. 如何修复最后两个调用?

    这取决于你的意图。

    【讨论】:

      【解决方案2】:

      字符串文字不是右值。 ()

      1. 如何修复最后两个调用?

      您可以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;长无符号整数 N = 5]
      void test(C (&)[1]) [with C = char]
      void test(C (&)[N]) [with C = char;长无符号整数 N = 5]
      void test(C (&&)[1]) [with C = char]
      void test(C (&&)[N]) [with C = char;长无符号整数 N = 5]
      void test(const C (&&)[1]) [with C = char]
      void test(const C (&&)[N]) [with C = char;长无符号整数 N = 5]

      【讨论】:

        【解决方案3】:

        谢谢@songyuanyao 的回答,我现在明白为什么最后两种情况选择了test(char*&amp;&amp;)。感谢@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
        

        【讨论】:

          猜你喜欢
          • 2011-01-03
          • 1970-01-01
          • 2022-12-05
          • 1970-01-01
          • 1970-01-01
          • 2020-01-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多