C ++中定义上下文和实例化点之间非依赖构造的解释差异

Differences of the interpretation of a non-dependent construct between definition context and point of instantiation in c++

N4527 14.6 [temp.res]/p8

If a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, the program is ill-formed; no diagnostic is required. If the interpretation of such a construct in the hypothetical instantiation is different from the interpretation of the corresponding construct in any actual instantiation of the template, the program is ill-formed; no diagnostic is required. [ Note: This can happen in situations including the following:

(8.1) — a type used in a non-dependent name is incomplete at the point at which a template is defined but is complete at the point at which an instantiation is performed, or

(8.2) — an instantiation uses a default argument or default template argument that had not been defined at the point at which the template was defined, or

(8.3) — constant expression evaluation (5.20) within the template instantiation uses

(8.3.1) — the value of a const object of integral or unscoped enumeration type or

(8.3.2) — the value of a constexpr object or

(8.3.3) — the value of a reference or

(8.3.4) — the definition of a constexpr function,

并且在定义模板时未定义该实体,或者

那么,这些代码格式不正确吗?

代码 1:

extern double b;

template<class T>
void f(T=b){}

void g(){
    f<double>();//ill-formed or not?
}

double b = 0;

void h(){
    f<double>();//ill-formed or not?
}

代码 2:

//translation 1
extern double b;

template<class T>
void f(T=b){}

void g(){
    f<double>();//ill-formed or not?
}

//translation 2
double b = 0;

Issue1850 Differences between definition context and point of instantiation

Various characteristics of entities referred to by a non-dependent reference in a template can change between the definition context and the point of instantiation of a specialization of that template. These include initialization (which affects whether an object can be used in a constant expression), function and template default arguments, and the completeness of types. There is implementation divergence as to whether these are checked in the definition context or at the point of instantiation. Presumably a rule is needed to make it ill-formed, no diagnostic required, if the validity of such a reference changes between the two contexts.

你能给我更多的例子来说明非依赖名称的特征在两种情况下有何不同吗?通常约为 8.2 和 8.3.1

这是一个例子:

extern const int b;

template<int, int>
void f(int);

template<int, const int &>
void f(long);

template<class>
void g() {
    f<0, b>(0);
}
// #1

extern const int b = 0;


int main(){
    g<int>(); 
}

// #2

#1 处的假设实例化将调用 void f<0, b>(long),因为此时 b 不是常量表达式,因此 (int) 重载 SFINAE。 #2 处的实例化(这是 g<int> 的实例化点)将调用 void f<0, 0>(int),因为那时 b 是常量表达式,(int) 重载是可行的并且赢得重载决议。

Clang and GCC will in fact call different fs with this code.