web-dev-qa-db-fra.com

Modèle avec déduction de retour de type automatique

J'ai créé une structure basée sur un modèle et j'essaie de surcharger les opérateurs binaires. Pour une raison quelconque, la fonction ne renvoie pas le type de données correct, même si le type est correctement converti entre.

template<typename T>
struct Number{
    const T value;
    Number(T a) : value(a) {}

    template<typename U>
    auto operator*(Number<U> other){
        auto new_value = value*other.value;
        std::cout << typeid(new_value).name() << std::endl;
        return Number(new_value);
    }
};

Maintenant, si je réalise cette opération avec le code suivant appelé dans main. Il retourne le numéro du type du premier, pas le numéro du type le plus élevé.

auto b =  Number<int>(6) * Number<double>(2.3); // this should be an int*double=double
std::cout << b.value << typeid(b.value).name() << std::endl;
auto c = Number<double>(2.3) * Number<int>(6);
std::cout << c.value << typeid(c.value).name() << std::endl;

La sortie est la suivante: D 13i D 13.8d

D'après ce que j'ai compris, le constructeur incorrect est appelé lorsque la fonction renvoie le nouveau Number(new_value). Je ne comprends pas comment et pourquoi cela se produit car new_value est du "type correct".

6
Helv

Dans l'étendue d'un modèle, le nom du modèle va représenter le nom de la classe injectée et non le modèle. Donc, il n'y aura pas de CTAD, et c'est à dessein

Utiliser return Number<decltype(new_value)>(new_value); est la solution de contournement simple.

9
StoryTeller

Vous retournez le premier type et non le second:

template<typename U>
auto operator*(Number<U> other){
    auto new_value = value*other.value;
    std::cout << typeid(new_value).name() << std::endl;
    return Number(new_value);
}

Même si new_value est un double, vous le stockez dans un Number<T>.

Essayer:

template<typename U>
auto operator*(Number<U> other){
    auto new_value = value*other.value;
    std::cout << typeid(new_value).name() << std::endl;
    return Number<decltype(new_value)>(new_value);
}
1
Matthieu Brucher

Comme StoryTeller souligne, dans une définition de modèle de classe, le nom de la classe fait référence à l'instance de classe spécifique (appelée injected-class-name) et non au nom du modèle.

Mais si vous souhaitez que la déduction d'argument de modèle de classe continue à s'appliquer, vous pouvez simplement qualifier le nom:

return ::Number(new_value);

::Number fait référence au modèle de classe Number et non au type spécifique Number<T>. Mais cela pourrait être un peu trop magique pour les autres lecteurs de votre code, et utiliser simplement Number<decltype(new_value)>(new_value) présente de nombreux avantages.

0
Barry