简体   繁体   English

可变参数模板的声明点

[英]Point of declaration for variadic template

At what point is a variadic template considered "declared"? 可变参数模板在什么时候被视为“已声明”? This compiles under clang++ 3.4, but not under g++ 4.8.2. 这将在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);
}

Apparently g++ won't match the function itself in the trailing return type. 显然,g ++在尾随返回类型中与函数本身不匹配。 The error from g++ 4.8.2 is: 来自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...));
                                                                      ^

Addendum: If I delete the declaration of the variadic template, both clang++ and g++ give errors. 附录:如果我删除可变参数模板的声明,则clang ++和g ++都会出错。

Addedum 2: I see that a similar question has been asked before. 附加2:我看到之前也曾问过类似的问题。 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(). 另外,我可以通过使用非原始参数sum()在POI上强制ADL使其与g ++一起使用。

Addendum 3: This works under both clang++ and 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());
}

As this question's answer (provided by Praetorian) indicates, the declaration is only complete after the return type, and GCC is correct. 正如这个问题的答案 (由Praetorian提供)表明,声明仅在返回类型之后才完整,并且GCC是正确的。 I believe clang's behavior is also allowable, but it's not portable. 我相信clang的行为也是允许的,但它不是可移植的。 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). 链接中的答案提供了使用traits类的解决方法,通常可以完成该工作,但是它有些笨拙,并且容易出错(因为您必须在单独的表达式中构造return类型,这可能与函数有细微的区别)表达)。 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. A上调用sum()时,您将应用用户定义的类型作为参数。 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. 这涉及依赖于参数的查找,这导致模板生成第二次查找A的名称空间(恰好是全局名称空间,与sum()相同sum()sum()重载,并且允许它在实例化过程中查找可变参数函数模板。

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. 因此,如果您可以将其中一个参数始终设置为需要ADL的用户定义类型,则可以在完全声明可变参数模板之后依靠重载解析的第二阶段来查找可变参数模板。 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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM