En C++ 11 et versions ultérieures, est-il autorisé de se spécialiser std::to_string
dans l'espace de noms std
pour les types personnalisés?
namespace std {
string to_string(::MyClass const & c) { return c.toString(); }
}
Exemple d'utilisation:
int main() {
MyClass c;
std::cout << std::to_string(c) << std::endl;
}
Dans C++ 11 et versions ultérieures, est-il autorisé de spécialiser std :: to_string dans l'espace de noms std pour les types personnalisés?
Non. Tout d'abord, ce n'est pas une fonction de modèle donc vous ne pouvez pas du tout la spécialiser.
Si vous demandez l'ajout de vos propres fonctions de surcharge, la réponse reste la même.
Extrait de documentation de Extension de l'espace de noms std :
Il est un comportement indéfini d'ajouter des déclarations ou des définitions à l'espace de noms std ou à tout espace de noms imbriqué dans std, à quelques exceptions près indiquées ci-dessous
Il est autorisé d'ajouter des spécialisations de modèle pour tout modèle de bibliothèque standard à l'espace de noms std uniquement si la déclaration dépend d'un type défini par l'utilisateur et que la spécialisation satisfait à toutes les exigences du modèle d'origine, sauf lorsque ces spécialisations sont interdites.
Dans la pratique, tout fonctionnera probablement très bien, mais à proprement parler, la norme dit qu'il n'y a aucune garantie de ce qui se passera.
Edit: Je n'ai pas accès à la norme officielle, donc ce qui suit est du projet de travail gratuit (N4296) :
17.6.4.2 Utilisation de l'espace de noms
17.6.4.2.1 Espace de noms std
- Le comportement d'un programme C++ n'est pas défini s'il ajoute des déclarations ou des définitions à l'espace de noms std ou à un espace de noms dans l'espace de noms std, sauf indication contraire. Un programme peut ajouter une spécialisation de modèle pour tout modèle de bibliothèque standard à l'espace de noms std uniquement si la déclaration dépend d'un type défini par l'utilisateur et que la spécialisation satisfait aux exigences de bibliothèque standard pour le modèle d'origine et n'est pas explicitement interdite.181
Le comportement d'un programme C++ n'est pas défini s'il déclare
2.1 - une spécialisation explicite de toute fonction membre d'un modèle de classe de bibliothèque standard, ou
2.2 - une spécialisation explicite de tout modèle de fonction membre d'une classe de bibliothèque standard ou d'un modèle de classe, ou
2.3 - une spécialisation explicite ou partielle de tout modèle de classe membre d'une classe de bibliothèque standard ou d'un modèle de classe.
Un programme ne peut instancier explicitement un modèle défini dans la bibliothèque standard que si la déclaration dépend du nom d'un type défini par l'utilisateur et que l'instanciation répond aux exigences de la bibliothèque standard pour le modèle d'origine.
- Une unité de traduction ne doit pas déclarer l'espace de noms std comme un espace de noms en ligne (7.3.1).
Si je ne me trompe pas, vous pouvez simplement surcharger to_string
pour un type générique:
template<typename T> to_string(const T& _x) {
return _x.toString();
}
et cela permet l'utilisation de l'ADL (recherche dépendante de l'argument) par votre programme pour choisir correctement le to_string
méthode basée sur le type transmis.
Le meilleur moyen serait de créer votre propre fonction qui utilise std::to_string
Si c'est possible ainsi que la méthode .toString()
chaque fois qu'elle est disponible pour l'argument passé:
#include <type_traits>
#include <iostream>
#include <string>
struct MyClass {
std::string toString() const { return "MyClass"; }
};
template<class T>
typename std::enable_if<std::is_same<decltype(std::declval<const T&>().toString()), std::string>::value, std::string>::type my_to_string(const T &t) {
return t.toString();
}
template<class T>
typename std::enable_if<std::is_same<decltype(std::to_string(std::declval<T&>())), std::string>::value, std::string>::type my_to_string(const T &t) {
return std::to_string(t);
}
int main() {
std::cout << my_to_string(MyClass()) << std::endl; // will invoke .toString
std::cout << my_to_string(1) << std::endl; //will invoke std::to_string
}
Dans C++ 11 et versions ultérieures, est-il autorisé de spécialiser std :: to_string dans l'espace de noms std pour les types personnalisés?
Non, vous ne pouvez pas ajouter de surcharge dans l'espace de noms std
pour to_string()
.
La bonne nouvelle est que vous n'en avez pas besoin, il existe une solution simple!
Vous pouvez fournir votre propre implémentation et laisser ADL (recherche dépendante des arguments) résoudre le problème pour vous.
Voici comment:
class A {};
std::string to_string(const A&)
{
return "A()";
}
int main()
{
A a;
using std::to_string;
std::cout << to_string(2) << ' ' << to_string(a);
}
Ici, nous avons utilisé le en utilisant la déclaration pour mettre std::to_string
Dans la portée, puis nous avons utilisé l'appel non qualifié à to_string()
.
Maintenant, std::to_string
Et ::to_string
Sont visibles et le compilateur sélectionne la surcharge appropriée.
Si vous ne voulez pas écrire using std::to_string
Avant d'utiliser to_string
À chaque fois ou si vous craignez d'oublier d'utiliser to_string
Sans l'espace de noms, vous pouvez créer une fonction d'aide
template<typename T>
std::string my_to_string(T&& t)
{
using std::to_string;
return to_string(std::forward<T>(t));
}
Notez que cette fonction peut être définie dans n'importe quel espace de noms et fonctionne indépendamment de l'espace de noms dans lequel les classes sont définies (elles ne doivent pas nécessairement être les mêmes).
Voir exemple .
[~ # ~] note [~ # ~] : cela fonctionne si vous êtes celui qui appelle to_string
. S'il existe une bibliothèque qui appelle std::to_string
Et que vous souhaitez la modifier pour vos types, vous n'avez pas de chance.