4 votes

Comment convertir implicitement n'importe quoi en chaîne de caractères ?

Mon objectif est de concevoir une classe String qui décore std::string afin de fournir certaines fonctionnalités dont mon programme a besoin. L'une des fonctionnalités que je souhaite ajouter est la possibilité de convertir implicitement n'importe quoi en String afin d'économiser de la frappe.

Afin de réaliser la conversion implicite, j'ai conçu la classe suivante :

std::ostream& operator<<(std::ostream& o, const String& s);

class String {
public:
    template<typename t_value>
    String::String(t_value value) {
       std::ostringstream oss;
       oss << value;
      _str = oss.str();
    }
private:
    std::string _str;
}

Cela fonctionne bien avec tous les types qui ont l'attribut << opérateur défini. Le problème est apparu avec toute classe qui n'a pas l'opérateur de flux. Une erreur de compilation serait acceptable, mais ce que j'ai obtenu est une récursion à l'infini puisque le C++ essaie d'utiliser ma classe globale << pour essayer de le convertir en mon type String.

Mon objectif principal est de coder comme ceci

class Foo {
    int _memberWithUnderscoreInName;
}

String s = Foo();

Et obtenir une erreur de compilation au lieu d'une boucle infinie dans le constructeur.

Existe-t-il une solution simple pour cela ?

5voto

Mike Seymour Points 130519

Au lieu de déclarer l'opérateur de sortie dans l'espace de noms environnant, déclarez-le uniquement en tant qu'ami de l'opérateur de sortie. String classe :

class String {
public:
    // This must be implemented inline here
    friend std::ostream& operator<<(std::ostream& o, const String& s) {
        return o << _str; // for example
    }

    template<typename t_value>
    String(t_value value) {
       std::ostringstream oss;
       oss << value;
      _str = oss.str();
    }
private:
    std::string _str;
};

Maintenant, il ne peut être trouvé que par une recherche dépendante de l'argument, et ne sera donc pris en compte que si le second argument est réellement de type String et non pas seulement convertible en elle. Par conséquent, il ne sera pas considéré comme un candidat à l'adhésion à l'UE. os << value dans le constructeur, donnant une erreur de compilation plutôt qu'une spirale de mort à l'exécution s'il n'y a pas d'autre candidat.

2voto

Ceci ou quelque chose de similaire devrait régler le problème :

namespace HasFormattedOutput {

    namespace Detail
    {
        struct Failure{};
    }

    template<typename OutputStream, typename T>
    Detail::Failure operator << (OutputStream&, const T&);

    template<typename OutputStream, typename T>
    struct Result : std::integral_constant<
        bool,
        ! std::is_same<
            decltype((*(OutputStream*)0) << std::declval<T>()),
            Detail::Failure
        >::value
    > {};
} // namespace HasFormattedOutput

template <typename T, typename OutputStream = std::ostream>
struct has_formatted_output
:   HasFormattedOutput::Result<OutputStream, T>
{};

class X  {
    public:
    X() {}

    template <typename T>
    X(const T& t) {
        static_assert(
             has_formatted_output<T>::value, 
             "Not supported type.");
        std::ostringstream s;
        s << t;
        str = s.str();
    }

    private:
    std::string str;
};
std::ostream& operator << (std::ostream& stream, const X&) { return stream; }

struct Y  {
    Y() {}
};

int main() {
    Y y;
    X x(y);
    return 0;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X