web-dev-qa-db-fra.com

Surcharge de fonction pour les littéraux de chaîne lvalue et rvalue reference

La fonction test ci-dessous est surchargée pour les chaînes vides lvalue, les chaînes non vides lvalue et les chaînes rvalue. J'ai essayé de compiler avec Clang et GCC mais dans les deux cas je n'ai pas le résultat escompté.

#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);
}

Sortie avec 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 *&&)

Sortie avec 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; }
      ^~~~

Mes questions sont:

  1. Quel compilateur est correct?
  2. Avec Clang, pourquoi test(str1) et test(str2) choisissent la surcharge rvalue alors qu'elles sont lvalues?
  3. Avec GCC, pourquoi l'appel test(str1) est ambigu?
  4. Existe-t-il une règle standard pour cette situation?
  5. Comment réparer les deux derniers appels?

Je vous remercie.

16
Baptistou
  1. Quel compilateur est correct?

GCC est correct.

  1. Avec clang, pourquoi str1 et str2 choisissent la surcharge rvalue alors qu'elles sont lvalues?

Clang a tort sur test(str1);, cela devrait être ambigu. Pour test(str2);, str2 Pourrait être converti implicitement en pointeur, c'est-à-dire la décroissance tableau-à-pointeur. Le char* Converti est une valeur r. Pour la même raison que # 3, les séquences de conversion implicites ont le même classement, alors la fonction non modèle est préférée; test(char*&&) est sélectionné.

  1. Avec gcc, pourquoi appeler avec str1 est ambigu?

Pour que test(const char (&)[1]) soit appelée, une conversion de qualification de char[1] En const char[1] Est requise; pour que test(char*&&) soit appelée, une conversion de tableau en pointeur est requise. Les deux sont qualifiés de correspondance exacte et ont le même classement.

  1. Existe-t-il une règle standard pour cette situation?

Voir le classement des séquences de conversion implicites dans la résolution de surcharge , et conversions implicites .

  1. Comment réparer les deux derniers appels?

Cela dépend de votre intention.

13
songyuanyao

Les littéraux de chaîne ne sont pas des valeurs r. ( )

  1. Comment réparer les deux derniers appels?

Vous pouvez tout lever l'ambiguïté avec les spécialisations de modèle:

#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));
}

donne

test de vide (const C (&) [1]) [avec C = char]
test de vide (const C (&) [N]) [avec C = char; long non signé int N = 5]
test de vide (C (&) [1]) [avec C = char]
test de vide (C (&) [N]) [avec C = char; long non signé int N = 5]
test de vide (C (&&) [1]) [avec C = char]
test de vide (C (&&) [N]) [avec C = char; long non signé int N = 5]
test de vide (const C (&&) [1]) [avec C = char]
test de vide (const C (&&) [N]) [avec C = char; long non signé int N = 5]

3
Darklighter

Merci @songyuanyao pour votre réponse, je comprends maintenant pourquoi test(char*&&) est choisie dans les deux derniers cas. J'ai pu lever toute ambiguïté avec la spécialisation des modèles lors de la première surcharge grâce à la réponse @Darklighter également.

J'ai donc résolu mon problème comme ci-dessous:

#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);
}

Production :

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