Recently I do a research about modern c++. I saw a video [in 49:00] about c++11/c++14 variadic templates. If you want to calculate the sum of tuple ofdifferent types(such as int
, double
) with variadic templates, using c++11, the video suggest a solution:
struct Sum
{
template<typename T>
static T sum(T n)
{
return n;
}
template<typename T, typename... Args>
static auto sum(T n, Args... rest) -> decltype(n+sum(rest...))
{
return n + sum(rest...);
}
}
auto x = Sum::sum(1, 2.5, 3);
auto
can not deduce return type in c++11
, so you have to declare return type using decltype
. But some compiler build failed, some compiler build successfully.link
Although, using auto to deduce return type
has no problem, my question is:
c++11
standard covered this question? If not, do the compilers deal with the problem by its own implementation?gcc 8.1
compile failed, whereas gcc 4/5/6/7
compile successfully? Is there any compatibility problem in gcc?By the way, the compile error message is:
test.cc:20:16: error: no matching function for call to 'sum'
double x = Sum::sum(1, 2.5, 3);
test.cc:12:17: note: candidate template ignored: substitution failure [with T = int, Args = ]: use of undeclared identifier 'sum'
static auto sum(T n, Args... rest) -> decltype(n + sum(rest...))
test.cc:6:14: note: candidate function template not viable: requires single argument 'n', but 3 arguments were provided
static T sum(T n)
1 error generated.
The function-name lookup in the trailing return type here,
template<typename T, typename... Args>
static auto sum(T n, Args... rest) -> decltype(n+sum(rest...))
is done in the context of immediately before this sum
is declared, plus via ADL (argument dependent lookup).
And as this template is not visible via ADL at the point of invocation on the types in question, it is proper for this to fail to compile.
The older gcc compilers probably used the object context in addition to the contexts they are supposed to. That is a reasonable error to make.
You can fix this easily:
struct Sum {
private:
template<typename T>
friend T summer(Sum, T n) {
return n;
}
template<typename T, typename... Args>
friend auto summer(Sum, T n, Args... rest) -> decltype(n+summer(Sum{},rest...)) {
return n + summer(Sum{},rest...);
}
public:
template<class...Args>
auto sum(Args...args)->decltype(summer(Sum{}, args...)){
return summer(Sum{}, args... );
}
};
here we force ADL in some private friends. This permits the overloads of summer to "see themselves" in their trailing return type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With