web-dev-qa-db-ja.com

文字列リテラルlvalueおよびrvalue参照の関数オーバーロード

以下の関数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. 最後の2つの呼び出しを修正する方法は?

ありがとうございました。

16
Baptistou
  1. どのコンパイラが正しいですか?

GCCは正しいです。

  1. Clangでは、str1とstr2が左辺値であるのに右辺値のオーバーロードを選択するのはなぜですか?

Clangはtest(str1);で間違っています。あいまいなはずです。 test(str2);の場合、_str2_は暗黙的にポインターに変換できます。つまり、配列からポインターへの減衰です。変換された_char*_は右辺値です。 #3と同じ理由で、暗黙の変換シーケンスは同じランキングを持ち、非テンプレート関数が優先されます。 test(char*&&)が選択されています。

  1. Gccでは、str1での呼び出しがあいまいなのはなぜですか。

test(const char (&)[1])を呼び出すには、_char[1]_から_const char[1]_への修飾変換が必要です。 test(char*&&)を呼び出すには、配列からポインタへの変換が必要です。どちらも 完全一致 として修飾され、同じランキングになります。

  1. この状況の標準ルールはありますか?

オーバーロード解決における暗黙的な変換シーケンスのランキング 、および 暗黙的な変換 を参照してください。

  1. 最後の2つの呼び出しを修正するにはどうすればよいですか?

それはあなたの意図に依存します。

13
songyuanyao

文字列リテラルは右辺値ではありません。 (

  1. 最後の2つの呼び出しを修正する方法は?

テンプレートの特殊化で すべてを明確化 できます。

#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])[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
Darklighter

@songyuanyaoの回答ありがとうございます。最後の2つのケースで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
1
Baptistou