39 votes

type de retour final utilisant decltype avec une fonction de modèle variadique

Je veux écrire un simple additionneur (pour rire) qui ajoute chaque argument et retourne une somme avec le type approprié. Actuellement, j'ai obtenu ceci:

#include <iostream>
using namespace std;

template <class T>
T sum(const T& in)
{
   return in;
}

template <class T, class... P>
auto sum(const T& t, const P&... p) -> decltype(t + sum(p...))
{
   return t + sum(p...);
}

int main()
{
   cout << sum(5, 10.0, 22.2) << endl;
}

Sur GCC 4.5.1 cela semble fonctionner très bien pour 2 arguments par exemple, somme(2, 5.5) renvoie à l'article 7.5. Cependant, avec plus d'arguments que ça, j'ai des erreurs que sum() n'est tout simplement pas encore définie. Si je déclare somme() comme ceci cependant:

template <class T, class P...>
T sum(const T& t, const P&... p);

Puis il travaille pour un nombre quelconque d'arguments, mais de la somme(2, 5.5) serait de retour entier 7, ce qui n'est pas ce que je m'attends. Avec plus de deux arguments, je suppose que decltype() serait de faire une sorte de récursivité pour être en mesure de déduire le type de t + sum(p,...). Est-ce légal de C++0x? ou ne decltype() uniquement avec les variadic déclarations? Si c'est le cas, comment voulez-vous écrire une telle fonction?

23voto

sellibitze Points 13607

Je pense que le problème est que les variadic modèle de fonction est uniquement considérée déclaré après que vous avez spécifié son type de retour de sorte que sum en decltype ne peut jamais se référer à la variadic fonction du modèle lui-même. Mais je ne suis pas sûr de savoir si c'est un bug de GCC ou C++0x n'a tout simplement pas permettre cela. Ma conjecture est que le C++0x ne permettent pas à une "récursive" appel à l' ->decltype(expr) partie.

Comme solution, nous pouvons éviter ce "récursive" appel en ->decltype(expr) avec une coutume classe de traits:

#include <iostream>
#include <type_traits>
using namespace std;

template<class T> typename std::add_rvalue_reference<T>::type val();

template<class T> struct id{typedef T type;};

template<class T, class... P> struct sum_type;
template<class T> struct sum_type<T> : id<T> {};
template<class T, class U, class... P> struct sum_type<T,U,P...>
: sum_type< decltype( val<const T&>() + val<const U&>() ), P... > {};

De cette façon, nous pouvons remplacer decltype dans votre programme avec typename sum_type<T,P...>::type et compiler.

Edit: Depuis ce qui renvoie en fait decltype((a+b)+c) au lieu de decltype(a+(b+c)) qui serait plus proche de la façon dont vous utilisez plus, vous pouvez remplacer la dernière spécialisation avec ceci:

template<class T, class U, class... P> struct sum_type<T,U,P...>
: id<decltype(
      val<T>()
    + val<typename sum_type<U,P...>::type>()
)>{};

8voto

Tomaka17 Points 2097

Apparemment, vous ne pouvez pas utiliser decltype de manière récursive (du moins pour le moment, peut-être qu'ils vont le réparer)

Vous pouvez utiliser une structure de modèle pour déterminer le type de la somme

Ça a l'air moche mais ça marche

 #include <iostream>
using namespace std;


template<typename... T>
struct TypeOfSum;

template<typename T>
struct TypeOfSum<T> {
    typedef T       type;
};

template<typename T, typename... P>
struct TypeOfSum<T,P...> {
    typedef decltype(T() + typename TypeOfSum<P...>::type())        type;
};



template <class T>
T sum(const T& in)
{
   return in;
}

template <class T, class... P>
typename TypeOfSum<T,P...>::type sum(const T& t, const P&... p)
{
   return t + sum(p...);
}

int main()
{
   cout << sum(5, 10.0, 22.2) << endl;
}
 

8voto

GingerPlusPlus Points 851

La solution de C ++14:

 template <class T, class... P>
auto sum(const T& t, const P&... p){
    return t + sum(p...);
}
 

Le type de retour est déduit automatiquement.

Voir le compilateur en ligne

3voto

davidhigh Points 827

Une autre réponse à la dernière question avec moins de frappe à l'aide de C++11 std::common_type: utilisez Simplement

std::common_type<T, P ...>::type

comme type de retour de votre variadic somme.

Concernant l' std::common_type, voici un extrait de http://en.cppreference.com/w/cpp/types/common_type:

Pour l'arithmétique types, le type le plus commun peut aussi être considéré comme le type (éventuellement en mode mixte) expression arithmétique telle que T0() + T1() + ... + Tn().

Mais évidemment, cela ne fonctionne que pour les expressions arithmétiques et ne règlent pas le problème général.

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