tipo de retorno à direita usando decltype com uma function de modelo variadic

Eu quero escrever um sumdor simples (para risos) que adiciona todos os argumentos e retorna uma sum com o tipo apropriado. Atualmente, tenho isto:

#include  using namespace std; template  T sum(const T& in) { return in; } template  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; } 

No GCC 4.5.1, isso parece funcionar bem para dois argumentos, por exemplo, sum (2, 5.5) retorna com 7.5. No entanto, com mais argumentos do que isso, recebo erros que sum () simplesmente não está definido ainda. Se eu declarar sum () assim:

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

Então ele funciona para qualquer número de argumentos, mas a sum (2, 5.5) retornaria o número inteiro 7, que não é o que eu esperaria. Com mais de dois argumentos eu assumo que decltype () teria que fazer algum tipo de recursion para poder deduzir o tipo de t + sum (p …). Isso é legal C ++ 0x? ou o decltype () só funciona com declarações não-variadicas? Se for esse o caso, como você escreveria tal function?

Eu acho que o problema é que o modelo de function variadic é considerado apenas declarado depois que você especificou seu tipo de retorno para que a sum em decltype nunca possa se referir ao próprio modelo de function variadic. Mas eu não tenho certeza se isso é um bug GCC ou C ++ 0x simplesmente não permite isso. Meu palpite é que o C ++ 0x não permite uma chamada “recursiva” na parte ->decltype(expr) .

Como uma solução alternativa, podemos evitar essa chamada “recursiva” em ->decltype(expr) com uma class de traços customizada:

 #include  #include  using namespace std; template typename std::add_rvalue_reference::type val(); template struct id{typedef T type;}; template struct sum_type; template struct sum_type : id {}; template struct sum_type : sum_type< decltype( val() + val() ), P... > {}; 

Desta forma, podemos replace o decltype em seu programa com o typename sum_type::type e ele compilará.

Edit: Como isso realmente retorna decltype((a+b)+c) vez de decltype(a+(b+c)) que estaria mais perto de como você usa a adição, você poderia replace a última especialização por:

 template struct sum_type : id() + val::type>() )>{}; 

Aparentemente você não pode usar o decltype de maneira recursiva (pelo menos no momento, talvez eles consertem)

Você pode usar uma estrutura de modelo para determinar o tipo da sum

Parece feio mas funciona

 #include  using namespace std; template struct TypeOfSum; template struct TypeOfSum { typedef T type; }; template struct TypeOfSum { typedef decltype(T() + typename TypeOfSum::type()) type; }; template  T sum(const T& in) { return in; } template  typename TypeOfSum::type sum(const T& t, const P&... p) { return t + sum(p...); } int main() { cout << sum(5, 10.0, 22.2) << endl; } 

Solução do C ++ 14:

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

O tipo de retorno é deduzido automaticamente.

Veja no compilador online

Outra resposta para a última pergunta com menos digitação usando std::common_type do C ++ 11: simplesmente use

 std::common_type::type 

como tipo de retorno de sua sum variadic.

Em relação a std::common_type , aqui está um trecho de http://en.cppreference.com/w/cpp/types/common_type :

Para tipos aritméticos, o tipo comum também pode ser visto como o tipo de expressão aritmética (possivelmente de modo misto), como T0 () + T1 () + … + Tn ().

Mas obviamente isso funciona apenas para expressões aritméticas e não cura o problema geral.

Eu ofereço essa melhoria para a resposta aceita. Apenas duas estruturas

 #include  template  struct sum_type { using type = decltype(std::declval

() + std::declval::type>()); }; template struct sum_type

{ using type = P; };

Agora apenas declare suas funções como

 template  auto sum(const T& in) -> T { return in; } template  auto sum(const P& t, const Ps&... ps) -> typename sum_type::type { return t + sum(ps...); } 

Com isso, seu código de teste agora funciona

 std::cout << sum(5, 10.0, 22.2, 33, 21.3, 55) << std::endl; 

146,5