“std::common_type”是关联性的

Is `std::common_type` associative?

本文关键字:关联性 std common type      更新时间:2023-10-16

模板类std::common_type计算可变参数类型列表的通用类型。它是使用三元运算符的返回类型x:y?z递归定义的。从这个定义来看,计算std::common_type<X,Y>是否具有关联性,即是否具有关联性,即计算是否

using namespace std;
static_assert( is_same<common_type< X, common_type<Y,Z>::type    >::type,
                       common_type<    common_type<X,Y>::type, Z >::type>::value, "" );

永远不会为is_same<...>表达式有效的所有类型的XYZ抛出编译时错误。

请注意,我不是在问是否

static_assert( is_same<common_type<X,Y>::type,
                       common_type<Y,X>::type>::value, "" );

会开火。显然不会。以上是一个完全不同的问题。

另请注意,std::common_type的规格在 C++14 中略有变化,可能会在 C++17 中再次更改。因此,对于不同版本的标准,答案可能会有所不同。

在MinGW-w64(gcc 4.9.1(上失败。在VS2013和(感谢Baum mit Augen(在gcc5.2或clang 3.7上使用libc ++上也失败。

#include <type_traits>
using namespace std;
struct Z;
struct X{operator Z();};
struct Y{operator X();};
struct Z{operator Y();};
static_assert( is_same<common_type<X,Y>::type,
                       common_type<Y,X>::type>::value, "" ); // PASS
static_assert( is_same<common_type<X,Z>::type,
                       common_type<Z,X>::type>::value, "" ); // PASS
static_assert( is_same<common_type<Y,Z>::type,
                       common_type<Z,Y>::type>::value, "" ); // PASS
static_assert( is_same<common_type< X, common_type<Y,Z>::type    >::type,
                       common_type<    common_type<X,Y>::type, Z >::type>::value, "" ); // FAIL...
#include <type_traits>
struct T2;
struct T1 {
    T1(){}
    T1(int){}
    operator T2();
};
struct T2 {
    operator int() { return 0; }
};
struct T3 {
    operator int() { return 0; }
};
T1::operator T2() { return T2(); }
using namespace std;
using X = T1;
using Y = T2;
using Z = T3;
int main()
{
    true?T2():T3(); // int
    static_assert(std::is_same<std::common_type_t<T2,
                                                  T3>,
                               int>::value,
                  "Not int");
    true?T1():(true?T2():T3()); // T1
    static_assert(std::is_same<std::common_type_t<T1,
                                                  std::common_type_t<T2,
                                                                     T3>>,
                               T1>::value,
                  "Not T1");
    // -----------------------------------------
    true?T1():T2(); // T2
    static_assert(std::is_same<std::common_type_t<T1,
                                                  T2>,
                               T2>::value,
                  "Not T2");
    true?(true?T1():T2()):T3(); // int
    static_assert(std::is_same<std::common_type_t<std::common_type_t<T1,
                                                                     T2>,
                                                  T3>,
                               int>::value,
                  "Not int");
    // -----------------------------------------
    static_assert( is_same<common_type_t< X, common_type_t<Y,Z>    >,
                           common_type_t<    common_type_t<X,Y>, Z > >::value,
                    "Don't match");
}

哎哟!这里的心理体操让我头疼,但我想出了一个无法编译的案例,在 ideone 上打印了"不匹配",带有 gcc 4.9.2 和"C++14"(gcc 5.1(。现在这是否合规是另一回事......

现在声明是针对类类型的,std::common_type_t<X, Y>应该是XY,但我强迫std::common_type_t<T2, T3>转换为int

请尝试使用其他编译器,让我知道会发生什么!

这不是关联性的!这是一个失败的程序:

#include <type_traits>
struct Z;
struct X { X(Z); }; // enables conversion from Z to X
struct Y { Y(X); }; // enables conversion from X to Y
struct Z { Z(Y); }; // enables conversion from Y to Z
using namespace std;    
static_assert( is_same<common_type< X, common_type<Y,Z>::type    >::type,
                       common_type<    common_type<X,Y>::type, Z >::type>::value, 
               "std::common_type is not associative." );

这个想法很简单:下图显示了common_type计算的内容:

    X,Y -> Y
    Y,Z -> Z
    X,Z -> X

第一行是合乎逻辑的,因为X可以转换为 Y ,反之亦然。其他两行也是如此。一旦XYZ组合并重新组合,我们就会得到Z。另一方面,将YZ以及与结果组合X相结合会得到X。因此,结果是不同的。

这成为可能的根本原因是可兑换性不是可传递的,即如果X可转换为Y并且Y可转换为Z则并不意味着X可转换为Z。如果可转换性是可传递的,那么转换将双向工作,因此无法明确计算common_type并导致编译时错误。

此推理独立于标准版本。它适用于C++11、C++14和即将到来的C++17。