web-dev-qa-db-fra.com

std :: to_string - plus qu'une instance de fonction surchargée correspond à la liste d'arguments

counter est un int

void SentryManager::add(std::string name,std::shared_ptr<Sentry>){
    name = name + std::to_string(counter);
}

Quelle serait la meilleure façon d'arrêter cette erreur? Quand j'étais paresseux, je viens de faire l'int long long (ou quelque chose), mais je suis sûr qu'il existe une meilleure façon de résoudre ce problème.

Message d'erreur:

sentrymanager.cpp(8): error C2668: 'std::to_string' : ambiguous call to overloaded function

J'utilise Visual C++ 2010 Express.

52
pighead10

Dans VC++ 2010, il y a trois surcharges de std::to_string qui prennent long long, unsigned long long, et long double, respectivement - clairement int n'est rien de tout cela, et aucune conversion n'est meilleure qu'une autre ( démo ), donc la conversion ne peut pas être effectuée implicitement/sans ambiguïté.

En termes de prise en charge réelle de C++ 11, il s'agit d'un échec de la part de l'implémentation de la bibliothèque standard de VC++ 2010 - la norme C++ 11 elle-même appelle en fait neuf surcharges de std::to_string ([string.conversions]/7):

string to_string(int val);
string to_string(unsigned val);
string to_string(long val);
string to_string(unsigned long val);
string to_string(long long val);
string to_string(unsigned long long val);
string to_string(float val);
string to_string(double val);
string to_string(long double val);

Si toutes ces surcharges avaient été présentes, vous n'auriez évidemment pas ce problème; cependant, VC++ 2010 n'était pas basé sur la norme C++ 11 réelle (qui n'existait pas encore au moment de sa sortie), mais plutôt sur N30 (from 2009 ), qui n'appelle pas ces surcharges supplémentaires. Par conséquent, il est difficile de blâmer VC++ aussi beaucoup ici ...

Dans tous les cas, pour seulement une poignée d'appels, il n'y a rien de mal à utiliser un cast pour résoudre l'ambiguïté vous-même:

void SentryManager::add(std::string& name, std::shared_ptr<Sentry>) {
    name += std::to_string(static_cast<long long>(counter));
}

Ou, en cas d'utilisation intensive de std::to_string dans votre base de code, écrivez quelques wrappers et utilisez-les à la place - de cette façon, aucune conversion de site d'appel n'est nécessaire:

#include <type_traits>
#include <string>

template<typename T>
inline
typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, std::string>::type
to_string(T const val) {
    return std::to_string(static_cast<long long>(val));
}

template<typename T>
inline
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, std::string>::type
to_string(T const val) {
    return std::to_string(static_cast<unsigned long long>(val));
}

template<typename T>
inline typename std::enable_if<std::is_floating_point<T>::value, std::string>::type
to_string(T const val) {
    return std::to_string(static_cast<long double>(val));
}

// ...

void SentryManager::add(std::string& name, std::shared_ptr<Sentry>) {
    name += to_string(counter);
}

Je ne peux pas vérifier si VC++ 2010 réussit ou échoue avec l'utilisation ci-dessus de SFINAE; en cas d'échec, les éléments suivants - en utilisant la répartition des balises au lieu de SFINAE - devraient être compilables (s'ils sont potentiellement moins clairs):

#include <type_traits>
#include <string>

namespace detail {
    template<typename T>                   // is_float         is_unsigned
    inline std::string to_string(T const val, std::false_type, std::false_type) {
        return std::to_string(static_cast<long long>(val));
    }

    template<typename T>                   // is_float         is_unsigned
    inline std::string to_string(T const val, std::false_type, std::true_type) {
        return std::to_string(static_cast<unsigned long long>(val));
    }

    template<typename T, typename _>       // is_float
    inline std::string to_string(T const val, std::true_type, _) {
        return std::to_string(static_cast<long double>(val));
    }
}

template<typename T>
inline std::string to_string(T const val) {
    return detail::to_string(val, std::is_floating_point<T>(), std::is_unsigned<T>());
}
86
ildjarn

Vous avez trébuché C++ DR 1261 , qui lit en partie

Le code "int i; to_string(i);" ne parvient pas à être compilé, car 'int' est ambigu entre 'long long' et 'long long unsigned '. Il semble déraisonnable de s'attendre à ce que les utilisateurs transtypent des nombres jusqu'à un type plus grand simplement pour utiliser to_string.

La résolution proposée consiste à ajouter davantage de surcharges. GCC l'a déjà implémenté ; Je suppose que MSVC ne l'a pas fait.

11
zwol