我想写一个简单的加法器(为giggles),它将每个参数相加并返回一个具有适当类型的和。 目前,我有这个:
#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()仅适用于非可变参数声明吗?如果是这样的话,你会怎么写这样的功能?
答案 0 :(得分:24)
我认为问题在于,在指定其返回类型后,可变参数函数模板仅被视为声明,以便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>()
)>{};
答案 1 :(得分:9)
C ++ 14的解决方案:
template <class T, class... P>
auto sum(const T& t, const P&... p){
return t + sum(p...);
}
自动扣除退货类型。
答案 2 :(得分:8)
显然你不能以递归的方式使用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 :(得分:3)
使用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()。
但显然这只适用于算术表达式,并不能解决一般问题。
答案 4 :(得分:1)
我对接受的答案提供了这种改进。只有两个结构
#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
答案 5 :(得分:0)
正确的做法:
#include <utility>
template <typename... Args>
struct sum_type;
template <typename... Args>
using sum_type_t = typename sum_type<Args...>::type;
template <typename A>
struct sum_type<A> {
using type = decltype( std::declval<A>() );
};
template <typename A, typename B>
struct sum_type<A, B> {
using type = decltype( std::declval<A>() + std::declval<B>() );
};
template <typename A, typename B, typename... Args>
struct sum_type<A, B, Args...> {
using type = sum_type_t< sum_type_t<A, B>, Args... >;
};
template <typename A>
sum_type_t<A> sum(A &&a)
{
return (std::forward<A>(a));
}
template <typename A, typename B>
sum_type_t<A, B> sum(A &&a, B &&b)
{
return (std::forward<A>(a) + std::forward<B>(b));
}
template <typename A, typename B, typename... C>
sum_type_t<A, B, C...> sum(A &&a, B &&b, C &&...args)
{
return sum( sum(std::forward<A>(a), std::forward<B>(b)), std::forward<C>(args)... );
}
https://coliru.stacked-crooked.com/a/a5a0e8019e40b8ba
这完全保留了运算的结果类型(甚至r值引用)。操作顺序很自然:(((a+b)+c)+d)
。
答案 6 :(得分:-1)
对于C ++ 17:
Exit: (11) member1(b, [b|_1206]) ? creep
Exit: (10) member1(b, [a, b|_1206]) ? creep
^ Fail: (9) not(user:member1(b, [a|_982])) ? creep