C++20 概念:成员函数偏序约束的显式实例化

C++20 Concepts: Explicit instantiation of partially ordered constraints for member functions

This 工作并输出“1”,因为函数的约束是部分排序的并且最受约束的重载获胜:

template<class T>
struct B {
    int f() requires std::same_as<T, int> {
        return 0;
    }
    int f() requires (std::same_as<T, int> && !std::same_as<T, char>) {
        return 1;
    }
};

int main() {
    std::cout << B<int>{}.f();
}

This 也可以工作,因为显式实例化不会实例化具有不满足约束的成员函数:

template<class T>
struct B {
    int f() requires std::same_as<T, int> {
        return 0;
    }
    int f() requires (!std::same_as<T, int>) {
        return 1;
    }
};

template struct B<int>;

那么this应该怎么办?

template<class T>
struct B {
    int f() requires std::same_as<T, int> {
        return 0;
    }
    int f() requires (std::same_as<T, int> && !std::same_as<T, char>) {
        return 1;
    }
};

template struct B<int>;

目前这会导致 trunk gcc 崩溃,因为它使用相同的 mangling 编译了两个函数。我认为只编译第二个函数是有意义的,这样行为就与常规的重载决议一致。标准中有任何内容处理这种极端情况吗?

C++20 认识到相同的有效要求可以有不同的拼写。所以标准定义了两个概念:“等效”和“功能等效”。

True "equivalence" 是基于满足 ODR(one-definition 规则):

Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one-definition rule, except that the tokens used to name the template parameters may differ as long as a token used to name a template parameter in one expression is replaced by another token that names the same template parameter in the other expression.

还有更多内容,但这不是问题。

模板头的等价性包括所有约束表达式都是等价的(模板头包括约束)。

功能等价(通常)是关于表达式的结果相等。对于模板头,two template heads that are not ODR equivalent can be functionally equivalent:

Two template-heads are functionally equivalent if they accept and are satisfied by ([temp.constr.constr]) the same set of template argument lists.

这部分基于约束表达式的有效性。

版本 1 和版本 3 中的两个模板头在 ODR 上并不等效,但它们在功能上是等效的,因为它们都接受相同的模板参数。如果它们是 ODR 等价的,那么该代码的行为将不同于它的行为。因此,this passage kicks in:

If the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required.

因此,所有编译器都同样正确,因为您的代码是错误的。显然,编译器不应该 straight-up 崩溃(并且应该作为错误提交),但是“ill-formed,不需要诊断”通常会带来无法预料的后果。