C++20 概念:当模板参数符合多个概念时,会选择哪个模板特化?
C++20 Concepts : Which template specialization gets chosen when the template argument qualifies for multiple concepts?
给定:
#include <concepts>
#include <iostream>
template<class T>
struct wrapper;
template<std::signed_integral T>
struct wrapper<T>
{
wrapper() = default;
void print()
{
std::cout << "signed_integral" << std::endl;
}
};
template<std::integral T>
struct wrapper<T>
{
wrapper() = default;
void print()
{
std::cout << "integral" << std::endl;
}
};
int main()
{
wrapper<int> w;
w.print(); // Output : signed_integral
return 0;
}
根据上面的代码,int
符合 std::integral
和 std::signed_integral
的概念。
令人惊讶的是,这会在 GCC 和 MSVC 编译器上编译并打印 "signed_integral"。我原以为它会因 "template specialization already been defined".
行的错误而失败
好吧,这是合法的,很公平,但为什么选择 std::signed_integral
而不是 std::integral
?当多个概念符合模板参数时,标准中是否定义了任何规则来选择模板专业化?
这是因为概念可以比其他概念更专业,有点像模板如何自行排序。这叫做partial ordering of constraints
在概念的情况下,当它们包含等效约束时,它们会相互包含。例如,下面是 std::integral
和 std::signed_integral
的实现方式:
template<typename T>
concept integral = std::is_integral_v<T>;
template<typename T> // v--------------v---- Using the contraint defined above
concept signed_integral = std::integral<T> && std::is_signed_v<T>;
规范化编译器将约束表达式归结为:
template<typename T>
concept integral = std::is_integral_v<T>;
template<typename T>
concept signed_integral = std::is_integral_v<T> && std::is_signed_v<T>;
在此示例中,signed_integral
完全意味着 integral
。所以从某种意义上说,有符号积分比积分“更受约束”。
标准是这样写的:
来自 [temp.func.order]/2(强调我的):
Partial ordering selects which of two function templates is more
specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type.
The deduction process determines whether one of the templates is more specialized than the other.
If so, the more specialized template is the one chosen by the partial ordering process.
If both deductions succeed, the partial ordering selects the more constrained template as described by the rules in [temp.constr.order].
这意味着如果一个模板有多个可能的替换并且都是从部分排序中选择的,它将select最受约束的模板。
A constraint P subsumes a constraint Q if and only if, for every disjunctive clause Pi in the disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in the conjunctive normal form of Q, where
a disjunctive clause Pi subsumes a conjunctive clause Qj if and only if there exists an atomic constraint Pia in Pi for which there exists an atomic constraint Qjb in Qj such that Pia subsumes Qjb, and
an atomic constraint A subsumes another atomic constraint B if and only if A and B are identical using the rules described in [temp.constr.atomic].
这描述了编译器用来对约束进行排序的包含算法,因此也描述了概念。
和Partial_ordering_of_constraints
A constraint P is said to subsume constraint Q if it can be proven that P implies Q up to the identity of atomic constraints in P and Q.
和
Subsumption relationship defines partial order of constraints, which is used to determine:
- the best viable candidate for a non-template function in overload resolution
- the address of a non-template function in an overload set
- the best match for a template template argument
- partial ordering of class template specializations
- partial ordering of function templates
和概念std::signed_integral
subsumes std::integral<T>
概念:
template < class T >
concept signed_integral = std::integral<T> && std::is_signed_v<T>;
所以你的代码没问题,因为 std::signed_integral
更 "specialized".
C++20 有一种机制来决定何时一个特定的受约束实体比另一个实体“更受约束”。这不是一件简单的事情。
这始于将约束分解为其原子组件的概念,这个过程称为 constraint normalization。它太大而且太复杂,无法进入这里,但基本思想是约束中的每个表达式都被递归地分解成它的原子概念部分,直到你到达一个不是概念的组件子表达式。
鉴于此,让我们看看 integral
和 signed_integral
的概念 are defined:
模板
概念积分=is_integral_v;
模板
概念 signed_integral = 积分 && is_signed_v;
integral
的分解就是is_integral_v
。 signed_integral
的分解为is_integral_v && is_signed_v
.
现在,我们进入constraint subsumption的概念。这有点复杂,但基本思想是,如果 C1 的分解包含 C2 中的每个子表达式,则称约束 C1“包含”约束 C2。我们可以看到 integral
不包含 signed_integral
,但是 signed_integral
包含 integral
,因为它包含所有 integral
] 确实如此。
接下来,我们开始对受限实体进行排序:
A declaration D1 is at least as constrained as a declaration D2 if
- D1 and D2 are both constrained declarations and D1's associated constraints subsume those of D2; or
- D2 has no associated constraints.
因为 signed_integral
包含 integral
,所以 <signed_integral> wrapper
与 <integral> wrapper
“至少一样受到约束”。但是,由于包含不可逆,反之则不然。
因此,根据“更多约束”实体的规则:
A declaration D1 is more constrained than another declaration D2 when D1 is at least as constrained as D2, and D2 is not at least as constrained as D1.
由于 <integral> wrapper
至少不像 <signed_integral> wrapper
那样受到限制,后者被认为比前者更受限制。
因此,当他们两个都可以申请时,更受限制的声明获胜。
请注意,当遇到不是 concept
的表达式时,约束包含规则 停止 。所以如果你这样做:
template<typename T>
constexpr bool my_is_integral_v = std::is_integral_v<T>;
template<typename T>
concept my_signed_integral = my_is_integral_v<T> && std::is_signed_v<T>;
在这种情况下,my_signed_integral
不会 包含 std::integral
。即使 my_is_integral_v
的定义与 std::is_integral_v
相同,因为它不是一个概念,C++ 的包含规则无法通过它来确定它们是否相同。
因此,包含规则鼓励您从对原子概念的操作中构建概念。
给定:
#include <concepts>
#include <iostream>
template<class T>
struct wrapper;
template<std::signed_integral T>
struct wrapper<T>
{
wrapper() = default;
void print()
{
std::cout << "signed_integral" << std::endl;
}
};
template<std::integral T>
struct wrapper<T>
{
wrapper() = default;
void print()
{
std::cout << "integral" << std::endl;
}
};
int main()
{
wrapper<int> w;
w.print(); // Output : signed_integral
return 0;
}
根据上面的代码,int
符合 std::integral
和 std::signed_integral
的概念。
令人惊讶的是,这会在 GCC 和 MSVC 编译器上编译并打印 "signed_integral"。我原以为它会因 "template specialization already been defined".
行的错误而失败好吧,这是合法的,很公平,但为什么选择 std::signed_integral
而不是 std::integral
?当多个概念符合模板参数时,标准中是否定义了任何规则来选择模板专业化?
这是因为概念可以比其他概念更专业,有点像模板如何自行排序。这叫做partial ordering of constraints
在概念的情况下,当它们包含等效约束时,它们会相互包含。例如,下面是 std::integral
和 std::signed_integral
的实现方式:
template<typename T>
concept integral = std::is_integral_v<T>;
template<typename T> // v--------------v---- Using the contraint defined above
concept signed_integral = std::integral<T> && std::is_signed_v<T>;
规范化编译器将约束表达式归结为:
template<typename T>
concept integral = std::is_integral_v<T>;
template<typename T>
concept signed_integral = std::is_integral_v<T> && std::is_signed_v<T>;
在此示例中,signed_integral
完全意味着 integral
。所以从某种意义上说,有符号积分比积分“更受约束”。
标准是这样写的:
来自 [temp.func.order]/2(强调我的):
Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process. If both deductions succeed, the partial ordering selects the more constrained template as described by the rules in [temp.constr.order].
这意味着如果一个模板有多个可能的替换并且都是从部分排序中选择的,它将select最受约束的模板。
A constraint P subsumes a constraint Q if and only if, for every disjunctive clause Pi in the disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in the conjunctive normal form of Q, where
a disjunctive clause Pi subsumes a conjunctive clause Qj if and only if there exists an atomic constraint Pia in Pi for which there exists an atomic constraint Qjb in Qj such that Pia subsumes Qjb, and
an atomic constraint A subsumes another atomic constraint B if and only if A and B are identical using the rules described in [temp.constr.atomic].
这描述了编译器用来对约束进行排序的包含算法,因此也描述了概念。
和Partial_ordering_of_constraints
A constraint P is said to subsume constraint Q if it can be proven that P implies Q up to the identity of atomic constraints in P and Q.
和
Subsumption relationship defines partial order of constraints, which is used to determine:
- the best viable candidate for a non-template function in overload resolution
- the address of a non-template function in an overload set
- the best match for a template template argument
- partial ordering of class template specializations
- partial ordering of function templates
和概念std::signed_integral
subsumes std::integral<T>
概念:
template < class T >
concept signed_integral = std::integral<T> && std::is_signed_v<T>;
所以你的代码没问题,因为 std::signed_integral
更 "specialized".
C++20 有一种机制来决定何时一个特定的受约束实体比另一个实体“更受约束”。这不是一件简单的事情。
这始于将约束分解为其原子组件的概念,这个过程称为 constraint normalization。它太大而且太复杂,无法进入这里,但基本思想是约束中的每个表达式都被递归地分解成它的原子概念部分,直到你到达一个不是概念的组件子表达式。
鉴于此,让我们看看 integral
和 signed_integral
的概念 are defined:
模板 概念积分=is_integral_v; 模板 概念 signed_integral = 积分 && is_signed_v;
integral
的分解就是is_integral_v
。 signed_integral
的分解为is_integral_v && is_signed_v
.
现在,我们进入constraint subsumption的概念。这有点复杂,但基本思想是,如果 C1 的分解包含 C2 中的每个子表达式,则称约束 C1“包含”约束 C2。我们可以看到 integral
不包含 signed_integral
,但是 signed_integral
包含 integral
,因为它包含所有 integral
] 确实如此。
接下来,我们开始对受限实体进行排序:
A declaration D1 is at least as constrained as a declaration D2 if
- D1 and D2 are both constrained declarations and D1's associated constraints subsume those of D2; or
- D2 has no associated constraints.
因为 signed_integral
包含 integral
,所以 <signed_integral> wrapper
与 <integral> wrapper
“至少一样受到约束”。但是,由于包含不可逆,反之则不然。
因此,根据“更多约束”实体的规则:
A declaration D1 is more constrained than another declaration D2 when D1 is at least as constrained as D2, and D2 is not at least as constrained as D1.
由于 <integral> wrapper
至少不像 <signed_integral> wrapper
那样受到限制,后者被认为比前者更受限制。
因此,当他们两个都可以申请时,更受限制的声明获胜。
请注意,当遇到不是 concept
的表达式时,约束包含规则 停止 。所以如果你这样做:
template<typename T>
constexpr bool my_is_integral_v = std::is_integral_v<T>;
template<typename T>
concept my_signed_integral = my_is_integral_v<T> && std::is_signed_v<T>;
在这种情况下,my_signed_integral
不会 包含 std::integral
。即使 my_is_integral_v
的定义与 std::is_integral_v
相同,因为它不是一个概念,C++ 的包含规则无法通过它来确定它们是否相同。
因此,包含规则鼓励您从对原子概念的操作中构建概念。