Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ variadic templates of different input type using c++11

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:

  1. Has c++11 standard covered this question? If not, do the compilers deal with the problem by its own implementation?
  2. Why do the newest version 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.

like image 796
ddming000 Avatar asked May 07 '18 03:05

ddming000


1 Answers

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.

like image 84
Yakk - Adam Nevraumont Avatar answered Oct 24 '22 09:10

Yakk - Adam Nevraumont