VS2010 中条件运算符的正确行为?

Posted

技术标签:

【中文标题】VS2010 中条件运算符的正确行为?【英文标题】:Conditional operator correct behaviour in VS2010? 【发布时间】:2010-09-15 21:23:26 【问题描述】:

我想知道这段代码是否表现出正确的 C++ 行为?

class Foo

public:
    Foo(std::string name) : m_name(name) 

    Foo(const Foo& other)  
        std::cout << "in copy constructor:" << other.GetName() << std::endl;
        m_name = other.GetName();
    

    std::string GetName() const  return m_name; 
    void SetName(std::string name)  m_name = name; 

private:
    std::string m_name;
;

Foo CreateFoo(std::string name)

    Foo result(name);
    return result;


void ChangeName(Foo& foo)

    foo.SetName("foofoo");


int _tmain(int argc, _TCHAR* argv[])

    Foo fooA("alan");
    std::cout << "fooA name: " << fooA.GetName() << std::endl;
    bool b = true;
    ChangeName(b ? fooA : CreateFoo("fooB"));
    std::cout << "fooA name: " << fooA.GetName() << std::endl;
    return 0;

在 VS2008 中内置时输出为:

fooA name: alan
fooA name: foofoo

但是当在 VS2010 中构建相同的代码时,它变成:

fooA name: alan
in copy constructor: alan
fooA name: alan

在“alan”上调用了一个复制构造函数,尽管是通过引用传递(或不是根据情况而定),但调用 ChangeName 并没有改变 fooA。

C++ 标准是否发生了变化,Microsoft 是否修复了错误行为或引入了错误?

顺便说一句,为什么要调用复制构造函数

【问题讨论】:

您的代码在我看来格式不正确,因此我不希望您有任何关于它的问题的答案。您希望将引用作为 ChangeName 的参数,但给它一个创建临时的表达式。 是的。将警告级别设置为 4 并再次编译。 @Noah:或者禁用 VC 编译器中的语言扩展,这将发出一个编译器错误,说明问题所在:error C2664: 'ChangeName' : cannot convert parameter 1 from 'Foo' to 'Foo &amp;' 不幸的是,禁用语言扩展与 /fp:fast 冲突 - 虽然技术上可以理解,但并不理想。 看到我不得不查一下,这里有关于 Eamon 评论的额外信息,供想要了解更多信息的人使用 - msdn.microsoft.com/en-us/library/e7s85ffb%28VS.80%29.aspx 【参考方案1】:

更完整的答案:

5.16/4&5:

"4 如果第二个和第三个操作数是左值并且具有相同的类型,则结果是该类型并且是左值。

5 否则结果为右值...."

换句话说,“bool ? lvalue:rvalue”会产生一个临时结果。

这将是它的结束,但是你将它传递给一个函数,根据 C++,必须接收一个左值作为参数。由于您将其传递为右值,因此您实际上拥有的代码不是 C++。 MSVC++ 接受它是因为它很愚蠢并且使用了一堆它不会告诉你的扩展,除非你把它变成一个挂件。由于您所拥有的并不是标准的 C++,而且 MS 只是通过扩展来允许它,因此对于它的“正确”内容,再也没有什么可说的了。

【讨论】:

Visual Studio 2010 确实允许绑定到 RVALue 的标准方式:'void ChangeName(Foo&& foo)'。这给出了与 OP 代码相同的行为。如果那是符合标准的,虽然我还没有弄清楚。【参考方案2】:

在您的条件表达式中,您的第二个操作数是Foo 类型的左值,而第三个是Foo 类型的右值(返回值函数不返回引用)。

这意味着条件的结果是 rvalue 而不是 lvalue (无论第一个表达式的值如何),然后您不能将其绑定到非常量引用。由于您违反了此规则,因此您无法调用语言标准来说明任一编译器版本的正确行为应该是什么。

如果第二个和第三个操作数都是相同类型的左值,则条件的结果是一个左值

编辑:从技术上讲,这两个版本都违反了标准,因为当您违反标准的可诊断规则时,它们都不会发出诊断。

【讨论】:

谢谢查尔斯。诺亚击败了你,但这个答案是正确的。【参考方案3】:

据我所知,C++ 标准在 5.16 第 3 点中涵盖了这一点,不是吗?

它说“如果 E2 是一个右值,或者如果不能进行上述转换:如果 E1 和 E2 具有类类型,并且基础类类型相同或者一个是另一个的基类:E1 可以是如果 T2 的类与 T1 的类是相同类型或基类,并且 T2 的 cv 限定与 cv 相同或大于 cv 限定,则转换为匹配 E2 - T1 的限定。如果应用转换,则 E1 更改为 T2 类型的右值,该右值仍引用原始源类对象(或其适当的子对象)。[注意:即不进行复制。]"

这不是描述了上面的情况吗?我认为确实如此,但如果有人能解释为什么没有,我愿意接受我可能是错的。

谢谢。

【讨论】:

只有当第二个和第三个操作数的类型不同时,整个段落才适用(“否则,如果第二个和第三个操作数的类型不同......”)。这里的类型是相同的,它们都是非常量、非易失性Foo,因此该段落不适用。 谢谢查尔斯 - 如果类型相同(右值/左值问题除外)我们应该最终得到一些非标准 C++,但如果类型不同,那么我觉得有点奇怪我们没关系(并且暗示引用) - 至少如果我理解正确的话。无论如何,感谢您的澄清。 条件表达式有一个lvalue和一个rvalue没有问题,只是表示表达式是一个rvalue我>。原始代码中的错误是尝试将此 rvalue 绑定到非常量引用;在这种情况下,这违反了标准。【参考方案4】:

奇怪的是,参考评论“是的。将警告级别设置为 4 并再次编译。” (诺亚罗伯茨),显然有一个警告,提供'ChangeName'采用非常量引用。如果这是一个接受 const 引用的函数,则不会发出警告,但仍会创建临时变量。或许这只是微软编译器的另一种说法。

【讨论】:

【参考方案5】:

编译器显然正在评估 : 的两边。

【讨论】:

不,不是。但是,要对表达式的结果进行类型解析,必须计算双方的 类型 编译器绝对不会在运行时评估双方。

以上是关于VS2010 中条件运算符的正确行为?的主要内容,如果未能解决你的问题,请参考以下文章

Shell中条件判断语法与判断条件

React中条件渲染和循环

thinkphp3.2 where 条件查询

thinkphp3.2 where 条件查询

17.自学Linux之路:bash编程之条件判断语句

VS 2012 中的条件运算符类型转换