конечный тип возврата с использованием decltype с функцией шаблона variadic - PullRequest
37 голосов
/ 19 сентября 2010

Я хочу написать простой сумматор (для смеха), который складывает каждый аргумент и возвращает сумму с соответствующим типом.В настоящее время у меня есть это:

#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;
}

В GCC 4.5.1 это, кажется, прекрасно работает для 2 аргументов, например, sum (2, 5.5) возвращает 7.5.Однако с большим количеством аргументов, чем это, я получаю ошибки, что sum () просто еще не определена.Однако, если я объявлю sum () следующим образом:

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

, тогда он будет работать для любого количества аргументов, но sum (2, 5.5) вернет целое число 7, а это не то, что я ожидал.Имея более двух аргументов, я предполагаю, что decltype () должен был бы выполнить какую-то рекурсию, чтобы иметь возможность определить тип t + sum (p ...).Это законно C ++ 0x?или decltype () работает только с невариантными объявлениями?Если это так, как бы вы написали такую ​​функцию?

Ответы [ 5 ]

24 голосов
/ 19 сентября 2010

Мне кажется, проблема в том, что шаблон функции переменной считается только объявленным после того, как вы указали тип возвращаемого значения, так что sum в decltype никогда не сможет ссылаться на сам шаблон функции переменной. Но я не уверен, что это ошибка GCC или C ++ 0x просто не позволяет этого. Мое предположение состоит в том, что C ++ 0x не допускает "рекурсивный" вызов в части ->decltype(expr).

В качестве обходного пути мы можем избежать этого «рекурсивного» вызова в ->decltype(expr) с помощью пользовательского класса черт:

#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... > {};

Таким образом, мы можем заменить decltype в вашей программе на typename sum_type<T,P...>::type, и она будет скомпилирована.

Редактировать: Так как это на самом деле возвращает decltype((a+b)+c) вместо decltype(a+(b+c)), что было бы ближе к тому, как вы используете сложение, вы можете заменить последнюю специализацию на эту:

template<class T, class U, class... P> struct sum_type<T,U,P...>
: id<decltype(
      val<T>()
    + val<typename sum_type<U,P...>::type>()
)>{};
9 голосов
/ 14 сентября 2014
Решение

C ++ 14:

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

Тип возврата вычитается автоматически.

Посмотреть в онлайн-компиляторе

8 голосов
/ 19 сентября 2010

Очевидно, что вы не можете использовать decltype рекурсивным способом (по крайней мере, на данный момент, может быть, они это исправят)

Вы можете использовать структуру шаблона для определения типа суммы

выглядит некрасиво, но работает

#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;
}
3 голосов
/ 14 сентября 2014

Еще один ответ на последний вопрос с меньшим набором текста с использованием C ++ 11 * std::common_type: просто используйте

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

в качестве типа возврата вашей переменной суммы.

Относительно std::common_type, вот выдержка из http://en.cppreference.com/w/cpp/types/common_type:

Для арифметических типов общий тип также может рассматриваться как тип арифметического выражения (возможно, смешанного режима), такого как T0 () + T1() + ... + Tn ().

Но очевидно, что это работает только для арифметических выражений и не решает общую проблему.

1 голос
/ 29 января 2018

Я предоставляю это улучшение принятому ответу.Всего две структуры

#include <utility>

template <typename P, typename... Ps>
struct sum_type {
    using type = decltype(std::declval<P>() + std::declval<typename sum_type<Ps...>::type>());
};

template <typename P>
struct sum_type<P> {
    using type = P;
};

Теперь просто объявите ваши функции как

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

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

С этим ваш тестовый код теперь работает

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

146.5

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...