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

How can I specialize std::common_type<A,B> so that it's naturally commutative?

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

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

std::common_type<T1, T2>::type and std::common_type<T2, T1>::type must denote the same type.

但是,common_type<T1, T2> 对于用户类型 T1T2:

可能是一个非常复杂的专业化
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>;
    };

}

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

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

这是我想出的 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> {};
    
}

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

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;
}

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

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

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