调用模板函数时出现歧义

Ambiguity while calling template function

本文关键字:歧义 函数 调用      更新时间:2023-10-16

我有以下问题:

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 << "callablen";
}
};

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

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

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

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

发生的情况是,在模板参数推导之后,我们有两个重载,这两个重载都是模板专业化的,并且基于转换(显然,这两种重载都是身份转换)同样好,因此重载解决必须求助于函数模板的偏序。

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

如果A是从函数参数包转换而来的,并且P不是参数包,类型推导失败否则,使用类型PA,然后按照中所述进行推导14.8.2.5.如果P是函数参数包,则将参数模板的每个剩余参数类型的类型A与键入函数参数包的声明符idP。每个比较推导中后续位置的模板参数由函数参数包扩展的模板参数包。如果对于给定类型(参数中的类型),推导成功模板被认为至少与来自参数模板。

(强调上方和下方的矿)

尝试从第一个过载推导到第二个过载,A是一个包,而P不是,因此适用上面段落中的第一句话;扣减失败。尝试以另一种方式推导,第三句和第四句适用,但推导再次失败,因为我们试图从一般形式callable的自变量推导形式A<T>的自变量。

因此,演绎是双向失败的;两个模板都不比另一个更专业;这通电话含糊不清。

该段的新措辞如下:

使用生成的类型PA,推导如下如14.8.2.5所述。如果P是函数参数包,则类型参数模板的每个剩余参数类型的A为与函数的声明符id的类型P相比参数包。每次比较都会推导的模板参数由展开的模板参数包中的后续位置功能参数包类似地,如果A从函数参数包,将其与剩余的每个参数进行比较参数模板的类型如果给定的类型,则参数模板中的类型被认为至少为与参数模板中的类型一样专用。

请注意,第一句话已经不见了,取而代之的是强调的一句,这允许从一个packA推断为一个非packP

现在,由于新规则,从A<T>callable的推导成功了,但反过来仍然失败(没有任何变化)。这使得第一个过载更加专业化。


快速修复:您可以在第一个过载中添加一个前导非封装参数:

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

这将避免对功能参数列表中第一个位置的包和非包进行比较。对于所有编译器来说,非包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 << "callablen";
}
};

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

这允许在没有参数的情况下调用operator()