Trailing return type с использованием метода decltype с функцией вариационного шаблона
Я хочу написать простой сумматор (для хихиканья), который добавляет каждый аргумент и возвращает сумму с соответствующим типом.
В настоящее время у меня есть это:
#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 аргументов, например. сумма (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...). Является ли это законным С++ 0x? или decltype() работает только с невариантными объявлениями? Если это так, как бы вы пишете такую функцию?
Ответы
Ответ 1
Я думаю, проблема в том, что шаблон вариационной функции считается объявленным только после того, как вы указали его тип возврата, чтобы sum
в decltype
никогда не ссылался на сам шаблон вариационной функции. Но я не уверен, является ли это ошибкой GCC или С++ 0x просто не позволяет этого. Я предполагаю, что С++ 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>()
)>{};
Ответ 2
По-видимому, вы не можете использовать 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:
template <class T, class... P>
auto sum(const T& t, const P&... p){
return t + sum(p...);
}
Тип возврата автоматически вычитается.
Посмотрите на онлайн-компилятор
Ответ 4
Другой ответ на последний вопрос с меньшим набором текста с помощью С++ 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().
Но, очевидно, это работает только для арифметических выражений и не излечивает общую проблему.