【问题标题】:Casting pointers and the ternary ?: operator. Have I reinvented the wheel?铸造指针和三元 ?: 运算符。我重新发明了轮子吗?
【发布时间】:2011-09-04 23:28:29
【问题描述】:

这段代码的最后一行使用castingAndTernary.cpp:15: error: conditional expression between distinct pointer types ‘D1*’ and ‘D2*’ lacks a cast编译失败

真正聪明的编译器不会有任何困难,因为两者都可以安全地转换为B*(基类)。我不愿意使用 static_cast 和 dynamic_cast 等等——我担心有一天我会混淆这些类并得到未定义的行为。这就是我创建 up_cast 模板的原因。此模板在允许的转换中做了最低限度的工作。有没有更简单的方法?还有其他解决方法,但我不禁想到还有更简单、更安全的方法可以使用?

struct B{ };
struct D1 : public B{ };
struct D2 : public B{ };

template<typename T,typename V>
T up_cast(V x) {
        return x;
}
int main() {
        bool boolean_expression = true;
        B * b;
        b = new D1;
        b = new D2;
        b = boolean_expression ? up_cast<B*>(new D1) : up_cast<B*>(new D2);
        b = boolean_expression ? new D1 : new D2;
}

g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3

更新根据@Konrad 的回答将名称从implicit_cast 更改为up_cast

【问题讨论】:

  • 短语“隐式转换”没有意义。强制转换是一种显式转换。
  • 对于更复杂的方法:b = boolean_expression ? new D1 : (true? new D2 : (B*)0 );
  • 我相信您的演员表在功能上与 C 风格演员表相同。
  • @Tomalak,我从here 得到了隐含这个词“'static_cast' 允许执行任何可以隐式执行的转换以及逆转换(甚至如果这不是隐式允许的)。” (我的重点)
  • @Aaron McDaid:如果你要这样做,我建议你明确指针,这样如果你在非指针上尝试它会失败(而不是潜在地复制) .考虑一下D1 d1; up_cast&lt;B&amp;&gt;(d1);,它看起来很合理,但不会像您期望的那样:template &lt;typename T, typename U&gt; T* up_cast( U* p ) { return p; } 应该可以解决问题。

标签: c++ casting ternary-operator


【解决方案1】:

真正聪明的编译器不会有任何困难,因为两者都可以安全地转换为B*

无关紧要。标准要求这种行为。一个真正聪明的编译器的行为与观察到的一样。

使用您的自定义转换实际上很好(而且您不愿意使用显式转换是正确的)。但是,我会使用不同的名称:upcast——因为这里发生了这种情况:在继承层次结构中向上转换。

【讨论】:

  • 我很好奇为什么编译器会被要求抱怨。尽管我承认在某些情况下编译器可能会变得非常复杂;具有多重继承,这将是许多有效的“最小公分母”类。
  • @Aaron:那么,编译器应该执行隐式转换到什么类型?
【解决方案2】:

我不打算回答,但在发表评论后我想,这是什么......这是一种与其他任何方法一样的方法:

int main() {
   bool condition = true;
   D1 d1;
   D2 d2;
   B * p = condition ? &d1 : (true? &d2 : p );
}

基本上滥用三元运算符来提取适当的类型。当编译器处理三元运算符时,它会尝试确定两个操作数是否可以隐式转换为公共类型1,如果可以,则使用该公共类型作为表达式的类型。

在上面的代码中,内部三元运算符:true? &amp;d2 : p 会尝试将表达式&amp;d2 的类型与p 的类型进行匹配,它会发现有一个简单的向上转换可以执行并且将该子表达式的返回类型设置为B*。请注意,因为条件是true,所以它总是会产生&amp;d2,即使它使用第三个参数来确定类型。

使用封闭表达式执行相同的操作,现在第二个参数是&amp;d1(类型D1*),第三个参数的类型是B*。同样,通过向上转换D1*,转换很简单,整个表达式的类型是B*

因为所有的转换都是编译器隐式执行的,如果你改变了指针的类型,打破了它们可以隐式转换的不变量,编译器会告诉你,解决抛出static_cast的问题在三元运算符的中间。

1 该标准规定了一组不同的转换,具体取决于参数的类型。在两个参数是指针的特殊情况下(如这里的情况),允许的转换是指针转换限定转换

【讨论】:

  • 我认为你的意思是“如果一个操作数可以转换为匹配另一个操作数的类型”。当您说“隐式转换为通用类型”时,我感到很惊讶,因为我发现这令人困惑,这意味着 condition ? &amp;d1 : &amp;d2 应该开箱即用。 +1 好技巧。
  • @Aaron:情况不同。在 David 的例子中,只有一个参数被转换为另一个参数的类型。在您的情况下,您需要对所有基类进行详尽的搜索。复杂性(和技巧)肯定是不同的。
  • @Aaron, @Matthieu:标准规定了每种情况下可以执行的转换。对于指针的特殊情况,允许的转换在脚注1中:指针转换限定转换。虽然编译器无法应用 any 转换来查找通用类型,但事实是结果类型可以是与任何参数不同的类型:D1 const * cp; B * b; (condition ? cp : b); 三元运算的结果case 是一个const B* 右值,既不是left 也不是right 类型。
【解决方案3】:

[三元]条件运算符要求其第二个和第三个操作数具有相同的类型。

b = boolean_expression ? new D1 : new D2;

您有不同的类型D1*D2*。如错误消息所示,您必须通过显式转换(即强制转换)确保正确的类型:

b = boolean_expression ? static_cast<B*>(new D1) : static_cast<B*>(new D2);

标准规定编译器必须要求这个(而不是仅仅进行隐式转换),所以这就是你的编译器所要求的。

【讨论】:

  • 我认为他知道这一点,并且只是在他的 implicit_cast&lt;&gt; 模板上要求 cmets...
  • @David:如果属实,他就跑题了,应该去 codereview。 :)
【解决方案4】:

我刚遇到这个问题,丢掉演员表,long 这样做是最干净的 IMO

B * d1 = new D1();
B * d2 = new D2();
B * b = boolean_expression ? d1 : d2;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-26
    • 2011-02-09
    • 2015-10-15
    • 2011-04-03
    • 2010-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多