最近,我发现GCC
偏序时的行为发生了变化,具体情况如下:
#include <iostream>
template<class T>
struct unknow_context{
using type = int;
};
template<class U>
void show(typename unknow_context<U>::type, U){ // candidate #1
std::cout<<"#1\n";
}
template<class T>
void show(int, T){ // candidate #2
std::cout<<"#2\n";
}
int main(){
show(0,0);
}
结果是,Clang打印#2
(任何版本Clang
都打印了一致的结果)。但是,GCC
具有不同的行为。旧版本的GCC打印#2
,相反,最新的GCC抱怨候选函数不明确。让我们看看标准对部分排序的看法。
temp.deduct.partial#2
推演过程使用转换后的类型作为参数模板,将另一个模板的原始类型作为参数模板。对于偏序比较中涉及的每种类型,此过程执行两次:一次使用转换后的模板 1 作为参数模板,模板 2 作为参数模板,再次使用转换后的模板 2 作为参数模板和模板 1作为参数模板。
#1
因此,我们可以分别得到候选和的两组 P/A 对#2
。一个是变换#1
后的A,原来#2
的P。另一个是原来#1
的P,变换后#2
的A。所以这两组将给出如下:
#a
|--------|------------------------------------------|
| P (#2) | A (#1) |
|--------|------------------------------------------|
| int | typename unknow_context<UniqueA>::type |
|--------|------------------------------------------|
| T | UniqueB |
|--------|------------------------------------------|
T
可以推导出来UniqueB
。对于第一组,规则说:
如果特定 P 不包含参与模板参数推导的模板参数,则该 P 不用于确定排序。
所以,我们先把它们放在一边,稍后再考虑。
#b
|----------------------------------|-------|
|P (#1) |A (#2) |
|----------------------------------|-------|
| typename unknow_context<U>::type |int |
|----------------------------------|-------|
| U |UniqueA|
|----------------------------------|-------|
对于非演绎的上下文,它的值可以从别处获得。
然而,在某些情况下,该值不参与类型推导,而是使用在其他地方推导或明确指定的模板参数的值。如果模板参数仅在非推导上下文中使用且未明确指定,则模板参数推导失败。
因此,我们可以忽略对typename unknow_context<U>::type
/ int
,并考虑U
/ UniqueA
。U
可以推导出来UniqueA
。
如果给定类型的推导成功,则参数模板中的类型被认为至少与参数模板中的类型一样特化。
从#b
中,我们得到#2
至少与 一样专业的结果#1
。
问题在#a set
. 第二对没有问题,可以说第二部分#1
至少和第二部分一样专业#2
。
但是,函数模板 F 是否比函数模板 G 更专业的规则定义为:
函数模板 F 至少与函数模板 G 一样特化,如果对于用于确定排序的每一对类型,来自 F 的类型至少与来自 G 的类型一样特化。如果 F 位于至少和 G 一样专业,而 G 至少不像 F 那样专业。
因此,我们注意 pair int / typename unknow_context<UniqueA>::type
,尽管规则说“P 不用于确定排序”。但是,标准中的重要说明说:
[ 注意:在 [temp.deduct.call] 和 [temp.deduct.partial] 下,如果 P 不包含出现在推导上下文中的模板参数,则不进行推导,因此P 和 A 不必具有相同的形式。——尾注]
所以,就目前而言,从P/A set #a
,#1
起码还是和#2
(演绎成功)一样专业。所以,我认为最新的GCC
应该是正确的(模棱两可,两者都不比另一个更专业)。
问题一:
哪个编译器是正确的?
问题2:
是否在部分排序期间执行特化的实例化?该标准似乎没有指定是否将执行实例化。
#include <iostream>
template<class T>
struct unknow_context{
using type = T;
};
template<class U>
void show(typename unknow_context<U>::type, U){
std::cout<<"#1\n";
}
template<class T>
void show(T, T){
std::cout<<"#2\n";
}
int main(){
show(0,0);
}
都选择了#2
。我担心其中的P/A pair
特殊之处,即:
|----|------------------------------------------------------------|
|P |A |
|----|------------------------------------------------------------|
|T |typename unknow_context<UniqueA>::type /*Is it equivalent to|
| | UniqueA? */ |
|----|------------------------------------------------------------|
|T |UniqueA |
|----|------------------------------------------------------------|
是否typename unknow_context<UniqueA>::type
会计算到UniqueA
?似乎所有编译器都将typename unknow_context<UniqueA>::type
其视为唯一类型,而不是将其计算为UniqueA
.