English 中文(简体)
C++20是否支持在另一个模板中提及模板后,使用限制宣布一个明确的功能模板即时?
原标题:Does C++20 support declaring an explicit function template instantiation using constraints after the template has been referenced in another template?

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,

  1. x<TypeA> s point of instantiation is at y<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.

  1. x<TypeA> s instantiation context contains y<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 ...

  1. 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.

  1. static_assert s evaluation triggers y<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?

最佳回答

你混淆了几个与模板有关的术语。

您所显示的法典中没有任何内容是 即时发射>。 <>>template,not,后附<

两者

template<typename T> consteval int f();

template<typename T> requires std::same_as<T, TypeB> consteval int f();

页: 1 两者都是功能模板的主要申报。 声明并不等同,因为它们没有等同的<代码>要求条款,因此宣布两个完全独立的功能模板,相互超载。

template<> consteval int f<TypeA>() { return 1; }

第一个模板有一个明确的专业化,因为它从template<>(在<之间不适用;>!)开始,并且由于仅明确列出模板参数,因此有可能根据先前申报的<代码>f功能模板的类型对“进行模范式扣减,并放弃其不存在的限制。

template<typename T> requires std::same_as<T, TypeB> consteval int f() { return 1; }

并不是一个明确的专业,因为它没有从<代码>template<>开始。 如上文所述,这是一份新功能模板超载声明。

关于明确专业声明的条款,首先允许有意使用。 而明确的专业化则与先前申报的职能模板相匹配,其方法是通过模板理由说明(包括制约性满意程度)对其职能类型进行扣减,部分订购职能模板(包括部分订购制约因素)。

尤其是,如果你在明确专业化之前宣布了这些职能模板的和>m”,那么明确的专业将是经过培训的<>功能模板,而不是像你的第一例那样没有受过训练的模板。


在您的第一例中,您称“无限制的<代码>x”(f)功能模板,该模板在<代码>y(g)上公布,并使用该功能模板的明确专业化。 只要在发出呼吁之前宣布明确专业化,就可宣布明晰专业化。

In your example under "Problem" f<TypeB>() is calling the first template, rather than the second one, because lookup of a dependent name from a template context is generally done from the point of definition. Only for ADL is lookup done from the point of instantiation. The lookup will therefore find only the unconstrained f function template, not the constrained one which is declared later and has no relation to the unconstrained one.

In your example at the end you are calling the constrained function template, since you now declared it before the use in g. You could remove the unconstrained declarations of f. This function template is unused.

In none of your examples is the constrained f function template explicitly specialized.

这两种情况都与即时点有关。

问题回答

暂无回答




相关问题
How to check at compile time that an expression is illegal?

I have a problem in my application where I d like to assert that a function application would be rejected by the compiler. Is there a way to check this with SFINAE? For example, assume that I d like ...

Why does concepts make C++ compile slower?

What kind of evil magic is it trying to do!?! I was listening to a Q&A session with herb sutter and one question was about concepts. Herb mention it made compilers slower (while the source ...

C++ template type which has a specific member method

What is a good way to call a member function of template type? Will the below foo() code only compile for types that have the bla() function defined? class A { void bla(); }; template<typename T&...

Order of c++ template arguments affecting compilation

I just encountered a new compilation error in my code after a visual studio update. I have stripped my code down to what I think is a minimal example. I have two templated functions both called pow - ...

热门标签