是否为参与偏序的类型执行实例化

jac*_*k X 5 c++ templates partial-ordering language-lawyer c++17

最近,我发现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);  
}
Run Code Online (Sandbox Code Playgroud)

结果是,Clang打印#2(任何版本Clang都打印出一致的结果)。但是,GCC具有不同的行为。旧版本的GCC打印#2,相反,最新的GCC抱怨候选函数不明确。让我们看看标准对偏序是怎么说的。
temp.deduct.partial#2

推导过程使用转换后的类型作为参数模板,使用另一个模板的原始类型作为参数模板。这个过程对偏序比较中涉及的每个类型进行两次:一次使用转换后的 template-1 作为参数模板和 template-2 作为参数模板,再次使用转换后的 template-2 作为参数模板和 template-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                                  |   
|--------|------------------------------------------|  
Run Code Online (Sandbox Code Playgroud)

T可以推导出来UniqueB。对于第一组,规则说:

如果特定 P 不包含参与模板参数推导的模板参数,则不使用该 P 来确定排序。

所以,我们先把它们放在一边,稍后再考虑

#b  
|----------------------------------|-------|
|P (#1)                            |A (#2) |   
|----------------------------------|-------|  
| typename unknow_context<U>::type |int    |  
|----------------------------------|-------|
| U                                |UniqueA|  
|----------------------------------|-------|
Run Code Online (Sandbox Code Playgroud)

对于非推导上下文,其值可以从别处获取。

然而,在某些上下文中,该值不参与类型推导,而是使用在其他地方推导或明确指定的模板参数的值。如果模板参数仅在非推导上下文中使用且未明确指定,则模板参数推导失败。

因此,我们可以忽略对typename unknow_context<U>::type/ int,而考虑U/ UniqueAU可以推导出来UniqueA

如果对给定类型的推导成功,则认为参数模板中的类型至少与参数模板中的类型一样专用。

从中#b,我们得到了#2至少与 一样专业的结果#1

问题在#a set. 第二对没有问题,我们可以说 的第二部分#1至少和 的第二部分一样专业#2

但是,函数模板 F 是否比函数模板 G 更专门化的规则定义为:

如果对于用于确定排序的每一对类型,函数模板 F 至少与函数模板 G 一样特化,则 F 中的类型至少与 G 中的类型一样特化。如果 F 位于,则 F 比 G 更特化至少与 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应该是正确的(模棱两可,两者都不比另一个更专业)。

问题 1:

哪个编译器是正确的?

问题2:

是否在偏序期间执行特化的实例化?该标准似乎没有指定是否将执行实例化。

#a  
|--------|------------------------------------------|
| P (#2) | A (#1)                                   |   
|--------|------------------------------------------|  
| int    |  typename unknow_context<UniqueA>::type  |
|--------|------------------------------------------|   
| T      | UniqueB                                  |   
|--------|------------------------------------------|  
Run Code Online (Sandbox Code Playgroud)

都选择了#2。我很关心其中的特殊性P/A pair,即:

|----|------------------------------------------------------------|
|P   |A                                                           |    
|----|------------------------------------------------------------|  
|T   |typename unknow_context<UniqueA>::type /*Is it equivalent to| 
|    | UniqueA? */                                                |  
|----|------------------------------------------------------------|   
|T   |UniqueA                                                     |  
|----|------------------------------------------------------------|  
Run Code Online (Sandbox Code Playgroud)

是否typename unknow_context<UniqueA>::type会被计算到UniqueA?似乎 All 编译器将其typename unknow_context<UniqueA>::type视为唯一类型,而不是将其计算为UniqueA.