如何专门化 std::common_type<A,B> 以便它自然可交换?

trb*_*abb 8 c++ type-traits c++-concepts c++20

std::common_type<T1, ..., TN>T1是 C++ 中的一个辅助模板,它可以找到所有...TN都可以隐式转换为的通用类型。

根据 C++ 规范,如果某些条件适用,用户可以专门化std::common_type<T1,T2>,并且:

std::common_type<T1, T2>::type并且std::common_type<T2, T1>::type必须表示相同的类型。

然而,common_type<T1, T2>对于用户类型来说,可能是一个非常复杂的专业化,T1并且T2

namespace std {

    template <typename T1, complicated_constraint_of<T1> T2, ...>
    struct common_type<complicated_expression_of<T1, ...>, complicated_expression_of<T2, ...>> {
        using type = complicated_type_expression_of<T1,T2>;
    };

}
Run Code Online (Sandbox Code Playgroud)

一般来说,约束表达式不一定是对称的(例如,我们可以指定 是T2的底T1)。这意味着为了保持对称性,我们需要用T1T2反转来重写整个特化,但在不犯任何错误的情况下做到这一点是极其困难和脆弱的。

如何common_type<T1,T2>为我自己的类型稳健地定义 的交换特化?

trb*_*abb 2

这是我想出的 C++20 解决方案:

// define concept of `common_type<A,B>` existing
template <typename A, typename B>
concept has_in_common_ordered = requires { common_type<A,B>::type; };

namespace std {
    
    // define common_type<A,B> if common_type<B,A>::type exists:
    template <typename A, has_in_common_ordered<A> B>
    struct common_type<A,B> : public common_type<B,A> {};
    
}
Run Code Online (Sandbox Code Playgroud)

现在,有了上面的内容,应该可以编译以下内容:

struct X {};
struct Y {};

namespace std {
    
    template<>
    struct common_type<X,Y> {
        using type = X; // or whatever
    };
    
}

int main() {
    // even though we only specialized for one ordering,
    // both orderings now exist:
    std::common_type<X,Y>::type a;
    std::common_type<Y,X>::type b;
}
Run Code Online (Sandbox Code Playgroud)

我认为以下两种情况之一是可能的:

  1. 这是一种自然的技术,实际上应该成为 的标准定义的一部分std::common_type
  2. 这是极其邪恶的,出于微妙的原因,你永远不应该这样做。

我等待有人在评论中告诉我它是哪一个。;)

  • `需要 { 类型名称 common_type&lt;A,B&gt;::type; }` 将更加惯用和可读。没有虚拟变量,没有随机的“true”。 (3认同)
  • 专业化的要求是“T1 和 T2 中至少一个取决于用户定义的类型”*。您对概念的部分专业化与该要求不符...... (3认同)