在可变参数模板方法和普通模板方法之间进行选择的规则是什么?

What are the rules for choosing between a variadic template method and a usual template method?

我在 class 中有两个模板运算符:

    template<class T>
    size_t operator()(const T& t) const {
        static_assert(boost::is_pod<T>(), "Not a POD type");
        return sizeof t;
    }

    template<typename... T>
    size_t operator()(const boost::variant<T...>& t) const
    {
        return boost::apply_visitor(boost::bind(*this, _1), t);
    }

我将 boost::variant<some, pod, types, here> 作为参数传递给这些运算符。 GCC 4.8 和 llvm 6.0 可以很好地编译代码,选择 boost::variant 参数化运算符。 gcc 4.7 选择 const T& t 参数化运算符,因此由于静态断言而无法编译。

那么,我有一个疑问,这两者之间的选择规则是什么? 我觉得gcc 4.7肯定有bug,不过我没有证据

一般来说,编译器只是将所有推导的模板实例化视为潜在的重载,选择 "best viable function"(§ 13.3.3)。

确实这意味着 GCC 4.7 有一个错误。

请参阅第 14.8.3 节:过载解决方案

描述所有模板实例都将作为任何非模板声明的重载加入候选集:

A function template can be overloaded either by (non-template) functions of its name or by (other) function templates of the same name. When a call to that name is written (explicitly, or implicitly using the operator notation), template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments. For each function template, if the argument deduction and checking succeeds, the template- arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the synthesized declarations and all of the non-template overloaded functions of the same name. The synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in 13.3.3.

就您的问题而言,重载最终无法区分(来源:@Piotr S)。在这种情况下,应用 "partial ordering" (§14.5.6.2):

F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2

请注意,事情可能会变得非常棘手,例如"open template" 版本采用了 T& 而不是 T const& (首选非 const 引用,其他条件相同)。

当您有多个重载最终具有相同的 "rank" 重载解析时,调用是 格式错误的 并且编译器将诊断出一个不明确的函数调用.

关键部分在[temp.deduct.partial]:

Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type and the transformed function type. [ Note: The creation of the transformed type is described in 14.5.6.2. —end note ] The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template. This process is done twice for each type involved in the partial ordering comparison: once using the transformed template-1 as the argument template and template-2 as the parameter template and again using the transformed template-2 as the argument template and template-1 as the parameter template.

即使对于 C++ 标准来说,这也确实很密集,但它的基本意思是这样的。以我们的两个重载为例:

template <class T> // #1
size_t operator()(const T& t) const

template <typename... T> // #2
size_t operator()(const boost::variant<T...>& t)

我们基本上会为每一个分配一些独特的类型,然后尝试看看另一个是否适用。因此,让我们为 #1 选择一些类型 A,为 #2 选择 B,C,Doperator()(const A&)#2 有用吗?否。operator()(const boost::variant<B,C,D>&) 是否适用于 #1?是的。因此,部分排序规则表明 #2#1 更专业。

因此,来自 [temp.func.order]:

The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.

来自[over.match.best]:

[A] viable function F1 is defined to be a better function than another viable function F2 if
— [..]
F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.

因此,在任何适用的情况下都应选择 #2。如果 GCC 选择 #1,那是不符合规范的行为,是一个错误。