调用模板函数时出现歧义

abr*_*ert 3 c++ templates ambiguity variadic-templates c++14

我有以下问题:

template< typename T >
class A
{};

class B
{
  public:
    template< typename... T >
    void operator()( A<T>... a )
    {
      std::cout << "A<T>\n";
    }

    template< typename callable >
    void operator()( callable f )
    {
      std::cout << "callable\n";
    }
};


int main()
{
  B      b;
  A<int> a;

  b( a );
}
Run Code Online (Sandbox Code Playgroud)

调用b( a )是模糊的 - 我期望输出A<T>,即执行第一个定义operator().有谁知道如何修理它?

bog*_*dan 9

直到并包括标准工作草案N4606(这包括已发布的C++ 11和C++ 14标准),调用确实含糊不清,并且Clang拒绝它是正确的.最新草案N4618中引入的更改使得部分排序规则选择了A<T>...过载.这是一个非常近期的变化; 我们需要给编译器一些时间来实现它.

MSVC 2015 U3和EDG 4.11选择A<T>...过载,因此它们之前不符合要求,并且在这方面神奇地符合最新草案.

会发生的是,在模板参数推导之后,我们有两个重载,它们都是模板特化,并且基于转换(两者的标识转换,显然)同样好,所以重载解析必须求助于函数模板的部分排序.

该过程在[temp.deduct.partial]的标准中描述,我们对第8段感兴趣.在N4618草案之前,它说:

如果A从函数参数包转换而P不是参数包,则类型推导失败.否则,使用所得到的类型PA,如在14.8.2.5描述的扣然后完成.如果P是函数参数包,则将A参数模板的每个剩余参数类型的类型与函数参数包Pdeclarator-id的类型进行比较.每个比较都推导出函数参数包扩展的模板参数包中后续位置的模板参数.如果对于给定类型的推导成功,则参数模板中的类型被认为至少与参数模板中的类型一样专用.

(强调我的上下)

试图从第一次超载到第二次超载A是一包而P不是,所以上段中的第一句适用; 扣除失败.另一种方式是尝试推论,第三和第四句适用,但是扣除再次失败,因为我们试图A<T>从一般形式的参数推断出形式参数的参数callable.

因此,演绎双向失败; 两种模板都不比另一种更专业; 这个电话很模糊.

该段的新措辞如下:

使用得到的类型PA,如14.8.2.5所描述的扣,然后完成.如果P是函数参数包,则将 A参数模板的每个剩余参数类型的类型与函数参数包的declarator-id的类型P进行比较.每个比较都推导出函数参数包扩展的模板参数包中后续位置的模板参数.类似地,如果A从函数参数包转换,则将其与参数模板的每个剩余参数类型进行比较.如果对于给定类型的推导成功,则参数模板中的类型被认为至少与参数模板中的类型一样专用.

请注意,第一句话已经消失,并被强调的句子取代,这允许从包装中扣除A到非包装P.

现在,从扣成功A<T>,以callable感谢新的规则,仍然未能周围的其他方式(没有什么变化).这使得第一次过载更加专业化.


快速修复:您可以向第一个重载添加一个前导的非包装参数:

template<class T, class... Ts> void operator()(A<T>, A<Ts>...)
Run Code Online (Sandbox Code Playgroud)

这将避免在函数参数列表中第一个位置的包和非包装之间进行比较.非包装A<T>显然比callable所有编译器更专业.

如果您需要一个匹配没有参数的调用的重载,请单独提供一个(抱歉...).