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

时间:2010-09-15 21:23:26

标签: c++ visual-c++ visual-studio-2010

我想知道这段代码是否表现出正确的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'上调用复制构造函数,尽管通过引用传递(或者不是视情况而定),fooA将被调用ChangeName保持不变。

C ++标准是否已更改,Microsoft是否修正了不正确的行为或是否引入了错误?

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

5 个答案:

答案 0 :(得分:5)

更全面的答案:

5.16 / 4和5:

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

5否则结果是右值....“

换句话说,“bool?lvalue:rvalue”会导致暂时的。

这将是它的结束,但是你将它传递给一个函数,根据C ++,它必须接收一个左值作为参数。因为你传递了一个rvalue,你实际上拥有的代码不是C ++。 MSVC ++接受它,因为它是愚蠢的,并使用一堆它没有告诉你的扩展,除非你把它变成一个挂件。既然你所拥有的不是标准的C ++,并且MS只是通过扩展允许它,那么就不再能说出关于它的“正确”了。

答案 1 :(得分:3)

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

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

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

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

答案 2 :(得分:0)

据我所知,这是由5.16第3点的C ++标准所涵盖的,不是吗?

它说“如果E2是一个右值,或者上面的转换不能完成:如果E1和E2有类型,并且底层类型相同或者一个是另一个的基类:E1可以是转换为匹配E2,如果T2的类型与T1类的基类相同,并且T2的cv资格与cv资格相同,或者cv资格等于cv - 如果应用转换,则E1被更改为类型T2的rvalue,它仍然引用原始源类对象(或其相应的子对象)。[注意:即,不进行复制。]“

这不能描述上述情况吗?我认为确实如此,但我愿意接受,如果有人可以解释为什么不这样做,我可能会错。

感谢。

答案 3 :(得分:0)

奇怪的是,参考评论“是的。将警告级别转为4并再次编译”。 (Noah Roberts),显然有一个警告,提供'ChangeName'采用非const引用。如果这是一个采用const-reference的函数,那么就没有警告,但仍然创建了临时变量。也许这只是微软编译器的另一个流行。

答案 4 :(得分:-1)

编译器显然正在评估:。