Point of declaration for variadic template

穿精又带淫゛_ 提交于 2019-12-30 09:52:27

问题


At what point is a variadic template considered "declared"? This compiles under clang++ 3.4, but not under g++ 4.8.2.

template <typename T>
const T &sum(const T &v) { return v; }

template <typename T, typename ... Ts>
auto sum(const T &v, const Ts & ... params) -> decltype(v + sum(params...));

template <typename T, typename ... Ts>
auto sum(const T &v, const Ts & ... params) -> decltype(v + sum(params...)) {
    return v + sum(params...);
}

int main() {
    sum(1, 2, 3);
}

Apparently g++ won't match the function itself in the trailing return type. The error from g++ 4.8.2 is:

sum.cpp: In function 'int main()':
sum.cpp:13:16: error: no matching function for call to 'sum(int, int, int)'
     sum(1, 2, 3);
                ^
sum.cpp:13:16: note: candidates are:
sum.cpp:2:10: note: template<class T> const T& sum(const T&)
 const T &sum(const T &v) { return v; }
          ^
sum.cpp:2:10: note:   template argument deduction/substitution failed:
sum.cpp:13:16: note:   candidate expects 1 argument, 3 provided
     sum(1, 2, 3);
                ^
sum.cpp:8:6: note: template<class T, class ... Ts> decltype ((v + sum(sum::params ...))) sum(const T&, const Ts& ...)
 auto sum(const T &v, const Ts & ... params) -> decltype(v + sum(params...)) {
      ^
sum.cpp:8:6: note:   template argument deduction/substitution failed:
sum.cpp: In substitution of 'template<class T, class ... Ts> decltype ((v + sum(sum::params ...))) sum(const T&, const Ts& ...) [with T = int; Ts = {int, int}]':
sum.cpp:13:16:   required from here
sum.cpp:5:74: error: no matching function for call to 'sum(const int&, const int&)'
 auto sum(const T &v, const Ts & ... params) -> decltype(v + sum(params...));
                                                                          ^
sum.cpp:5:74: note: candidate is:
sum.cpp:2:10: note: template<class T> const T& sum(const T&)
 const T &sum(const T &v) { return v; }
          ^
sum.cpp:2:10: note:   template argument deduction/substitution failed:
sum.cpp:5:74: note:   candidate expects 1 argument, 2 provided
 auto sum(const T &v, const Ts & ... params) -> decltype(v + sum(params...));
                                                                      ^

Addendum: If I delete the declaration of the variadic template, both clang++ and g++ give errors.

Addedum 2: I see that a similar question has been asked before. I guess the real question here is why it works with one compiler and not the other. Also, I can make it work with g++ by forcing ADL at the POI by using non-primitive arguments to sum().

Addendum 3: This works under both clang++ and g++:

class A {
};
A operator+(const A &, const A &) {
    return A();
}

template <typename T>
const T &sum(const T &v) { return v; }

/*
template <typename T, typename ... Ts>
auto sum(const T &v, const Ts & ... params) -> decltype(v + sum(params...));
*/

template <typename T, typename ... Ts>
auto sum(const T &v, const Ts & ... params) -> decltype(v + sum(params...)) {
    return v + sum(params...);
}

int main() {
    //sum(1, 2, 3);
    sum(A(), A(), A());
}

回答1:


As this question's answer (provided by Praetorian) indicates, the declaration is only complete after the return type, and GCC is correct. I believe clang's behavior is also allowable, but it's not portable. The answer in the link gives a workaround using a traits class, and that will usually do the job, but it's somewhat awkward and can be error prone (since you have to construct the return type in a separate expression which may differ subtlely from the function expression). Another possible workaround involves making your function a static member of a class template (and then adding a free function that forwards to the static template member).

There is another workaround that you can consider, which is hinted at in your second example which works on both compilers. When you call sum() on A, you are applying a user-defined type as a parameter. This involves argument-dependent-lookup, which causes the template generation to look a second time for overloads of sum() in the namespace of A (which happens to be the global namespace, the same as sum()), and this allows it to find the variadic function template during instantiation.

Thus, if you can arrange for one of your arguments to always be a user-defined type requiring ADL, you can then rely on the second phase of overload resolution to find the variadic template after it's fully declared. So, perhaps something like this might meet your need:

namespace sum_impl {
    struct Dummy { };

    template <typename T>
    T const &sum_helper(Dummy, T const &v) { return v; }

    template <typename T, typename ... Ts>
    auto sum_helper(Dummy d, T const &v, Ts const &...params)
            -> decltype(v + sum_helper(d, params...)) {
        return v + sum_helper(d, params...);
    }

    template<typename... P>
    auto sum( P const &...p )
            -> decltype( sum_helper( Dummy{}, p... ) {
        return sum_helper( Dummy{}, p... );
    }
}
using sum_impl::sum;


来源:https://stackoverflow.com/questions/23002364/point-of-declaration-for-variadic-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!