变量模板的声明点

Point of declaration for variadic template

本文关键字:声明 变量      更新时间:2023-10-16

变量模板在什么时候被视为"已声明"?这是在clang++3.4下编译的,但不是在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);
}

显然g++在后面的返回类型中与函数本身不匹配。g++4.8.2的错误为:

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...));
                                                                      ^

附录:如果我删除varadic模板的声明,clang++和g++都会出现错误。

附录2:我看到以前也有人问过类似的问题。我想这里真正的问题是为什么它能用一个编译器而不能用另一个编译器。此外,我可以通过使用sum()的非基元参数在POI处强制ADL,使其与g++一起工作。

附录3:这在clang++和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());
}

正如这个问题的答案(由Praetorian提供)所示,只有在返回类型之后声明才是完整的,GCC是正确的。我相信clang的行为也是可以允许的,但它是不可移植的。链接中的答案提供了一个使用traits类的解决方法,这通常可以完成任务,但这有点尴尬,而且可能容易出错(因为你必须在一个单独的表达式中构造返回类型,这个表达式可能与函数表达式有细微的不同)。另一种可能的解决方法是使函数成为类模板的静态成员(然后添加一个转发给静态模板成员的自由函数)。

您可以考虑另一种变通方法,这在第二个示例中有所暗示,该示例适用于两个编译器。当您在A上调用sum()时,您正在将用户定义的类型应用为参数。这涉及到依赖于参数的查找,这导致模板生成在A的命名空间(恰好是全局命名空间,与sum()相同)中第二次查找sum()的重载,并且这允许它在实例化期间找到可变函数模板。

因此,如果你可以安排你的一个参数始终是一个需要ADL的用户定义类型,那么你就可以在重载解析的第二阶段找到变量模板。所以,也许这样的东西可以满足你的需求:

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;