调用模板函数时出现歧义
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()
。
我有以下问题:
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 andP
is not a parameter pack, type deduction fails. Otherwise, using the resulting typesP
andA
, the deduction is then done as described in 14.8.2.5. IfP
is a function parameter pack, the typeA
of each remaining parameter type of the argument template is compared with the typeP
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
andA
, the deduction is then done as described in 14.8.2.5. IfP
is a function parameter pack, the typeA
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, ifA
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()
。