c++ - C++20 概念:当模板参数符合多个概念时,选择哪个模板特化?
问题描述
给定:
#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”。我期待它会因“模板专业化已经定义”的错误而失败。
好的,这是合法的,足够公平,但为什么std::signed_integral
选择而不是std::integral
?当多个概念符合模板参数的条件时,标准中是否定义了任何规则以及选择模板专业化的规则?
解决方案
这是因为概念可以比其他概念更专业,有点像模板如何为自己排序。这称为约束的部分排序
在概念的情况下,当它们包含等效约束时,它们相互包含。例如,以下是实现方式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(强调我的):
部分排序通过依次转换每个模板(参见下一段)并使用函数类型执行模板参数推导来选择两个函数模板中的哪一个比另一个更专业。推演过程确定模板中的一个是否比另一个更专业。如果是这样,更专业的模板是部分排序过程选择的模板。 如果两个推导都成功,则部分排序选择[temp.constr.order]中的规则描述的更受约束的模板。
这意味着如果一个模板有多个可能的替换,并且两者都是从偏序中选择的,它将选择最受约束的模板。
一个约束P 包含一个约束Q当且仅当,对于 P 的析取范式中的每个析取子句P i ,P i 包含 Q 的析取范式中的每个析取子句Q j,其中
当且仅当在P i中存在原子约束P ia且在Q j中存在原子约束Q jb使得P ia包含Q jb时,分离子句P i包含连接子句Q j,并且
当且仅当A和B使用[temp.constr.atomic]中描述的规则相同时,原子约束A包含另一个原子约束B。
这描述了编译器用来排序约束的包含算法,因此也描述了概念。
推荐阅读
- javascript - 如何在 React 中为非实例方法编写测试用例
- sql - sql中的子字符串匹配
- javascript - 我必须在 javascript 文件中添加 java 代码
- bash - 以存储在变量中的命令返回的值退出
- javascript - 客户关系管理起点
- python - ImportError 即使已安装 Python 模型(使用 gcloud)
- c# - 如何利用 MVVM Light EventToCommand 在 XAML 中绑定大量相同的事件?
- c# - 如果选择了第一个选项,jQuery 将多选所有值
- c# - 是否可以使用没有类型参数的泛型类?
- java - 无法在 Java 中创建临时文件