首页 > 解决方案 > 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::integralstd::signed_integral概念。

令人惊讶的是,这会在 GCC 和 MSVC 编译器上编译并打印“signed_integral”。我期待它会因“模板专业化已经定义”的错误而失败。

好的,这是合法的,足够公平,但为什么std::signed_integral选择而不是std::integral?当多个概念符合模板参数的条件时,标准中是否定义了任何规则以及选择模板专业化的规则?

标签: c++language-lawyerc++20c++-concepts

解决方案


这是因为概念可以比其他概念更专业,有点像模板如何为自己排序。这称为约束的部分排序

在概念的情况下,当它们包含等效约束时,它们相互包含。例如,以下是实现方式std::integralstd::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]中的规则描述的更受约束的模板。

这意味着如果一个模板有多个可能的替换,并且两者都是从偏序中选择的,它将选择最受约束的模板。

[temp.constr.order]/1

一个约束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,并且

  • 当且仅当AB使用[temp.constr.atomic]中描述的规则相同时,原子约束A包含另一个原子约束B。

这描述了编译器用来排序约束的包含算法,因此也描述了概念。


推荐阅读