【问题标题】:Is `std::common_type` associative?`std::common_type` 是关联的吗?
【发布时间】:2016-03-28 14:48:37
【问题描述】:

模板类std::common_type 计算一个通用类型到一个可变类型列表。 It is defined using the return type of the ternary operator x:y?z recursively. 从该定义来看,计算 std::common_type<X,Y> 是否具有关联性对我来说并不明显,即。 e.是否

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&lt;...&gt; 表达式有效的所有类型XYZ,永远不会抛出编译时错误。

请注意,我不是在问是否

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

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

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

【问题讨论】:

  • 您是否会接受(X,(Y,Z)) 具有通用类型但(X,Y)(X,Y),Z 没有的情况?或者您是否希望它们都解析为一种类型,但不同的类型。因为我很难为后者构建一个案例,并且强烈怀疑这是不可能的;任何两个转换链都会导致歧义。
  • static_assertion肯定会编译失败,因为没有通用类型。我假设问题是询问所有common_type 类型都有效但is_same 为假的情况。
  • @JonathanWakely 感谢您的提示。修正了问题。
  • 显然你可以让它不关联。但这没关系,如果它不具有关联性,那是因为您的设计中存在逻辑不一致。这就像以一种不符合逻辑的方式定义operator==
  • 我认为这更像是一种学术兴趣,但无论哪种方式,我都不同意“那没关系......你的设计存在逻辑上的不一致”,因为(在一般意义上)并不总是需要关联性,并且可能会出现替换失败。我至少可以想到一个与operator== 参数相切的反例:向下转换为浮点数可能等于另一个浮点数,但是向上转换为双精度的浮点数很容易使相等失败。 (使用这个 impl。保证文档,但是将两者都转换为 LCM(此处为浮点数)或两者都转换为 GCM(此处为双精度)以强制执行 assoc。是极端情况 spackle。)

标签: c++ templates c++11 types metaprogramming


【解决方案1】:

这在 MinGW-w64(gcc 4.9.1) 上失败。在 VS2013 和 gcc5.2 上(感谢 Baum mit Augen)或带有 libc++ 的 clang 3.7 上也失败。

#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...

【讨论】:

  • 使用 gcc5.2 和使用 libc++ 的 clang 3.7 也失败。
  • 在 clang 3.8.0 和 gcc 7.0.0(快照)上也失败了
【解决方案2】:
#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&lt;X, Y&gt; 应该是 XY,但我已强制 std::common_type_t&lt;T2, T3&gt; 转换为 int

请尝试使用其他编译器并告诉我会发生什么!

【讨论】:

  • 也使用 clang3.7 + libc++ 触发。
  • 也与 mingw-w64 一起触发。 VS2013 触发所有这些(以及一些其他错误),除了“Not T2”之一。当然,VS 不知道它是否符合标准......
  • 我对标准的解释表明,第二种情况格式不正确,因为 int 和 T1 可以相互转换。这使得它们没有资格成为 operator:? 的操作数 2 和 3:?
  • @RichardHodges int 转换为 T1T1 不会转换为 int(一个用户定义的仅转换规则)。
【解决方案3】:

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

#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。因此结果是不同的。

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

这个推理独立于标准版本。它适用于 C++11、C++14 和即将推出的 C++17。

【讨论】:

  • 是的,但我对三个类别有哲学上的担忧,它们不能相互映射,但只能周期性地映射。我想说这在语言中不是关联的,但它应该是,在同样的意义上,operator== 应该以T a = b; assert(a == b); 的方式实现
猜你喜欢
  • 2014-03-25
  • 2011-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-31
  • 2014-11-10
  • 1970-01-01
相关资源
最近更新 更多