调用模板函数时出现歧义

Ambiguity while calling template function

我有以下问题:

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 );
}

调用 b( a ) 不明确——我期望输出 A<T>,即 operator() 的第一个定义的执行。有人知道怎么解决吗?

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

MSVC 2015 U3 和 EDG 4.11 选择了 A<T>... 重载,因此它们之前 non-conforming 并且在这方面神奇地符合最新草案。

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

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

If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails. Otherwise, using the resulting types P and A, the deduction is then done as described in 14.8.2.5. If P is a function parameter pack, the type A of each remaining parameter type of the argument template is compared with the type P of the declarator-id of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template.

(强调我的上下)

试图从第一个重载推导到第二个,A是一个包而P不是,所以上面段落中的第一句话适用;推演失败。尝试反过来推导,第三和第四句适用,但推导再次失败,因为我们试图从一般形式 callable 的参数推导出 A<T> 形式的参数的参数。

因此,推论双向都失败了;两个模板都不比另一个更专业;调用不明确。

该段的新措辞如下:

Using the resulting types P and A, the deduction is then done as described in 14.8.2.5. If P is a function parameter pack, the type A of each remaining parameter type of the argument template is compared with the type P of the declarator-id of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. Similarly, if A was transformed from a function parameter pack, it is compared with each remaining parameter type of the parameter template. If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template.

请注意,第一句不见了,取而代之的是强调的句子,这允许从包 A 推导到 non-pack P.

现在,由于新规则,从 A<T> 推导成功到 callable,反之仍然失败(没有改变)。这使得第一个重载更加专业化。


快速修复:您可以向第一个重载添加前导 non-pack 参数:

template<class T, class... Ts> void operator()(A<T>, A<Ts>...)

这将避免比较函数参数列表中第一个位置的包和 non-pack。对于所有编译器,non-pack A<T> 显然比 callable 更专业。

如果您需要与不带参数的调用相匹配的重载,请单独提供一个(抱歉...)。

谢谢。

我解决问题如下:

#include <iostream>

template< typename T >
class A
{};

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

  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 );
    b( );
}

这样也可以不带参数调用 operator()