我想知道这段代码是否表现出正确的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是否修正了不正确的行为或是否引入了错误?
顺便说一句,为什么要调用复制构造函数?
答案 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)
编译器显然正在评估:。