Context
在C++,我们似乎能够在另一个模板功能中参照功能模板后,宣布/确定明确的功能模板即时,如果其他模板功能尚未用于专门类型,则我们能够宣布/确定这种模板。
template<typename T> consteval int x() { return 0; }
template<typename T> consteval int y() { return x<T>(); }
struct TypeA {};
template<> consteval int x<TypeA>() { return 1; }
static_assert(y<TypeA>() == 1); // PASS
In the above example, my reasoning to why the static_assert
succeeds is,
x<TypeA>
s point of instantiation is aty<TypeA>
s instantiation
n4868 [temp.point]/1 For a function template specialization ..., if the specialization is implicitly instantiated because it is referenced from within another template specialization and the context from which it is referenced depends on a template parameter, the point of instantiation of the specialization is the point of instantiation of the enclosing specialization.
x<TypeA>
s instantiation context containsy<TypeA>
s instantiation context
n4868 [module.context]/3 During the implicit instantiation of a template whose point of instantiation is specified as that of an enclosing specialization ([temp.point]), the instantiation context is the union of the instantiation context of the enclosing specialization ...
static_assert
evaluates the enclosed constant-expression
n4868 [temp.inst]/8 The existence of a definition of a variable or function is considered to affect the semantics of the program if the variable or function is needed for constant evaluation by an expression ([expr.const]), even if constant evaluation of the expression is not required or if constant expression evaluation does not use the definition.
static_assert
s evaluation triggersy<TypeA>
s instantiation
n4868 [temp.inst]/5 Unless a function template specialization is a declared specialization, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist or if the existence of the definition affects the semantics of the program.
Problem
C++20 引入了模板限制:[temp.constr.constr]
with the requires
keyword to specified restrictions on models. 当我们宣布/确定我们在使用<条码>g条码>后的限制的专业时,上述明确专业模式似乎不再奏效。
#include <concepts>
template<typename T> consteval int f() { return 0; }
template<typename T> consteval int g() { return f<T>(); }
struct TypeB {};
template<typename T> requires std::same_as<T, TypeB> consteval int f() { return 1; }
static_assert(g<TypeB>() == 1); // FAIL
从标准角度来看,这种模式(在上述样本中)是否得到支持? 在我看来,利用上述推理,我们应当支持这两个方面,而只有第一个样本汇编在海合会/海委会/洛佩克。
任何帮助/引导都将受到高度赞赏。
Additional Context
涉及制约因素的单壳没有汇编到X86-64海合会(13.2)、MSVC(v19.37)和LV(7.0.1)和C++20。
AST
仔细研究由伦敦考试和测验中心制作的深海考察,
|-FunctionTemplateDecl <line:3:1, col:52> col:36 f
| |-TemplateTypeParmDecl <col:10, col:19> col:19 typename depth 0 index 0 T
| |-FunctionDecl <col:22, col:52> col:36 consteval f int () implicit-inline
| | `-CompoundStmt <col:40, col:52>
| | `-ReturnStmt <col:42, col:49>
| | `-IntegerLiteral <col:49> int 0
| `-FunctionDecl <col:22, col:52> col:36 used consteval f int () implicit-inline
| |-TemplateArgument type TypeB
| | `-RecordType TypeB
| | `-CXXRecord TypeB
| `-CompoundStmt <col:40, col:52>
| `-ReturnStmt <col:42, col:49>
| `-IntegerLiteral <col:49> int 0
...
|-FunctionTemplateDecl <line:7:1, col:84> col:68 f
| |-TemplateTypeParmDecl <col:10, col:19> col:19 referenced typename depth 0 index 0 T
| |-ConceptSpecializationExpr <col:31, col:52> bool Concept 0xc3c4268 same_as
...
| | |-TemplateArgument <line:7:44> type T
| | | `-TemplateTypeParmType T dependent depth 0 index 0
| | | `-TemplateTypeParm T
| | `-TemplateArgument <col:47> type TypeB : TypeB
| | `-ElaboratedType TypeB sugar
| | `-RecordType TypeB
| | `-CXXRecord TypeB
| `-FunctionDecl <col:54, col:84> col:68 consteval f int () implicit-inline
| `-CompoundStmt <col:72, col:84>
| `-ReturnStmt <col:74, col:81>
| `-IntegerLiteral <col:81> int 1
it appears we implicitly instantiated f<TypeB>
first using the generic f<T>
, we then instantiated it again using f<T> requires std::same_as<T, TypeB>
=> breaks one-definition-rule, undefined behavior
Forward declare for the specialization with constraints
如果我们先宣布功能模板专门化有局限性,汇编者就不再产生对<代码>f<TypeB>-> 0的含蓄即时。
#include <concepts>
template<typename T> consteval int f() { return 0; }
struct TypeB {};
template<typename T> requires std::same_as<T, TypeB> consteval int f();
template<typename T> consteval int g() { return f<T>(); }
template<typename T> requires std::same_as<T, TypeB> consteval int f() { return 1; }
static_assert(g<TypeB>() == 1); // PASS
It seems to indicate the instantiation point of g<TypeB>
is at g
s definition time, i.e., g<TypeB>
is instantiated (and uses the name lookup & declarations available) at template<typename T> consteval int g() {...}
instead of at the static_assert
.
However, this behavior is different from the first snippet where we directly specialize template<> consteval int x<TypeA>() { return 1; }
instead of using template constraints. Is such difference is intended by the standard?