可变参数模板的声明点

时间:2014-04-11 02:18:43

标签: c++ templates c++11 variadic-templates trailing-return-type

什么时候被称为“声明”的可变参数模板?这在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...));
                                                                      ^

附录:如果我删除了可变参数模板的声明,则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());
}

1 个答案:

答案 0 :(得分:2)

由于this question's answer(由Praetorian提供)表明,声明仅在返回类型之后完成,并且GCC是正确的。我相信clang的行为也是允许的,但它不可移植。链接中的答案给出了一个使用traits类的解决方法,它通常会完成这项工作,但它有点笨拙并且容易出错(因为你必须在单独的表达式中构造返回类型,这可能与函数有细微差别)表达)。另一种可能的解决方法是让您的函数成为类模板的静态成员(然后添加一个转发给静态模板成员的自由函数)。

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

因此,如果您可以安排一个参数始终是需要ADL的用户定义类型,那么您可以依赖重载决策的第二阶段在完全声明后找到可变参数模板。所以,这样的事情可能会满足你的需求:

sum()