web-dev-qa-db-fra.com

C ++ 11 make_pair avec les paramètres de modèle spécifiés ne se compile pas

Je jouais juste avec g ++ 4.7 (l'un des derniers clichés) avec -std = c ++ 11 activé. J'ai essayé de compiler une partie de ma base de code existante et un cas qui a échoué me confond quelque peu.

J'apprécierais que quelqu'un puisse expliquer ce qui se passe.

Voici le code:

#include <utility>
#include <iostream>
#include <vector>
#include <string>

int main ( )
{
    std::string s = "abc";

    // 1 ok
    std::pair < std::string, int > a = std::make_pair ( s, 7 );

    // 2 error on the next line
    std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );

    // 3 ok
    std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );

    return 0;
}

Je comprends que make_pair est signifié à utiliser comme cas (1) (si je spécifie les types, alors je pourrais aussi bien utiliser (3)), mais je ne comprends pas pourquoi il échoue ce cas.

L'erreur exacte est:

test.cpp: In function ‘int main()’:
    test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)’
    test.cpp:11:83: note: candidate is:
    In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
                 from test.cpp:1:
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note:   template argument deduction/substitution failed:
    test.cpp:11:83: note:   cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’

Encore une fois, la question ici est simplement "que se passe-t-il?" Je sais que je peux résoudre le problème en supprimant la spécification du modèle, mais je veux juste savoir ce qui échoue ici sous les couvertures.

  • g ++ 4.4 compile ce code sans problème.
  • La suppression de -std = c ++ 11 se compile également avec du code sans problème.
75
vmpstr

Ce n'est pas ainsi que std::make_pair est destiné à être utilisé; vous n'êtes pas censé spécifier explicitement les arguments du modèle.

Le C++ 11 std::make_pair prend deux arguments, de type T&& et U&&, où T et U sont des paramètres de type modèle. Effectivement, cela ressemble à ceci (en ignorant le type de retour):

template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);

Quand vous appelez std::make_pair et spécifiez explicitement les arguments du type de modèle, aucune déduction d'argument n'a lieu. Au lieu de cela, les arguments de type sont substitués directement dans la déclaration de modèle, ce qui donne:

[return type] make_pair(std::string&& argT, int&& argU);

Notez que ces deux types de paramètres sont des références rvalue. Ainsi, ils ne peuvent se lier qu'aux valeurs r. Ce n'est pas un problème pour le deuxième argument que vous passez, 7, car il s'agit d'une expression rvalue. s, cependant, est une expression lvalue (ce n'est pas temporaire et elle n'est pas déplacée). Cela signifie que le modèle de fonction ne correspond pas à vos arguments, c'est pourquoi vous obtenez l'erreur.

Alors, pourquoi cela fonctionne-t-il lorsque vous ne spécifiez pas explicitement ce que T et U sont dans la liste des arguments du modèle? En bref, les paramètres de référence rvalue sont spéciaux dans les modèles. En partie à cause d'une fonction de langage appelée refonte de référence, un paramètre de référence rvalue de type A&&, où A est un paramètre de type de modèle, peut se lier à tout type de A.

Peu importe que A soit une lvalue, une rvalue, une constante, une volatile qualifiée ou non qualifiée, une A&& peut se lier à cet objet (encore une fois, si et seulement si A est lui-même un paramètre de modèle).

Dans votre exemple, nous appelons:

make_pair(s, 7)

Ici, s est une valeur de type std::string et 7 est une valeur r de type int. Étant donné que vous ne spécifiez pas les arguments de modèle pour le modèle de fonction, la déduction des arguments de modèle est effectuée pour déterminer quels sont les arguments.

Pour lier s, une valeur l, à T&&, le compilateur déduit que T est std::string&, donnant un argument de type std::string& &&. Cependant, il n'y a aucune référence aux références, donc cette "double référence" s'effondre pour devenir std::string&. s est une correspondance.

Il est simple de lier 7 à U&&: le compilateur peut déduire U comme int, donnant un paramètre de type int&&, qui se lie avec succès à 7 car il s'agit d'une valeur r.

Il y a beaucoup de subtilités avec ces nouvelles fonctionnalités de langage, mais si vous suivez une règle simple, c'est assez simple:

Si un argument de modèle peut être déduit des arguments de fonction, laissez-le être déduit. Ne fournissez pas explicitement l'argument à moins que vous ne le deviez absolument.

Laissez le compilateur faire le travail, et 99,9% du temps, ce sera exactement ce que vous vouliez de toute façon. Lorsque ce n'est pas ce que vous vouliez, vous obtiendrez généralement une erreur de compilation qui est facile à identifier et à corriger.

121
James McNellis