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

时间:2015-02-03 20:58:12

标签: c++ templates gcc boost

我在课堂上有两个模板操作符:

    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,但我没有任何证据。

2 个答案:

答案 0 :(得分:3)

关键部分位于[temp.deduct.partial]:

  

两组类型用于确定部分排序。对于涉及的每个模板   原始函数类型和转换后的函数类型。 [注意:创建转换后的类型   在14.5.6.2中描述。 -end note]演绎过程使用变换后的类型作为参数   模板和其他模板的原始类型作为参数模板。这个过程完成了两次   对于部分排序比较中涉及的每种类型:一次使用转换后的模板-1作为   参数模板和模板-2作为参数模板,并再次使用转换后的模板-2   作为参数模板,模板-1作为参数模板。

即使对于C ++标准来说,它真的很密集,但它基本上意味着什么呢。采取我们的两个重载:

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

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

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

所以,来自[temp.func.order]:

  

演绎过程确定其中一个模板是否比另一个模板更专业。如果   因此,更专业的模板是部分订购流程选择的模板。

来自[over.match.best]:

  

[A]可行函数F1被定义为比另一个可行函数更好的函数   F2如果是    - [..]
   - F1F2是函数模板特化,F1的函数模板更专业   根据14.5.6.2中描述的部分排序规则,F2的模板。

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

答案 1 :(得分:0)

通常,编译器只会将所有推断的模板实例化视为潜在的重载,选择“最佳可行函数”(第13.3.3节)。

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

参见§14.8.3:重载决议

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

  

函数模板可以通过其(非模板)函数重载   名称或(其他)同名的功能模板。当打电话给那个   写入名称(显式或隐式使用运算符表示法),   模板参数推导(14.8.2)和检查任何显式模板   为每个函数模板执行参数(14.3)以找到模板   可以与该函数模板一起使用的参数值(如果有)   实例化一个可以用它调用的函数模板特化   调用参数。对于每个函数模板,如果参数推论和   检查成功,使用模板参数(推断和/或显式)   合成单个函数模板特化的声明   添加到要在重载中使用的候选函数集   分辨率即可。如果对于给定的函数模板,参数推导失败,则为no   这样的函数被添加到该模板的候选函数集中。的的   完整的候选函数集包括所有合成声明   和所有同名的非模板重载函数。的的   合成声明的处理方式与余数中的任何其他函数一样   重载分辨率,除非在13.3.3中明确指出。

对于你的问题,重载最终无法区分( credit:@Piotr S )。在这种情况下,应用“部分排序”(§14.5.6.2):

  

F1和F2是功能模板专精,F1的功能模板比F2的模板更专业

请注意,事情可能会非常棘手,例如“开放模板”版本采用T&代替T const&(非const引用是首选,其他条件相同)。

如果有多个重载最终具有相同的“排名”以进行重载解析,则调用格式错误并且编译器将诊断模糊函数调用。