10
string foo() { return "hello"; }
int main() 
{
    //below should be illegal for binding a non-const (lvalue) reference to a rvalue
    string& tem  = foo();   

    //below should be the correct one as only const reference can be bind to rvalue(most important const)
    const string& constTem = foo();   
}
  1. GCC 是给出编译错误std::string&的好方法:从临时类型的非 const 类型引用的无效初始化std::string
  2. VS2008 还不错,因为至少它给出了一个编译警告:警告 C4239:使用了非标准扩展:'initializing':从转换std::string为非std::string &const 引用只能绑定到左值
  3. 问题来了 - VS2010(SP1) 编译良好,没有任何错误或警告,为什么??!!我知道VS2010 中的右值引用可用于与右值绑定,但我没有使用&&,而是在演示代码中,我只是使用非 const 左值引用!

有人能帮我解释一下这里 VS2010 的行为吗?它是一个错误!?谢谢

4

4 回答 4

12

这是 VS 编译器的一个已知问题/功能。他们一直允许这样做,并且似乎没有任何推动删除该扩展名的努力。

于 2011-08-25T11:18:07.010 回答
9

编译器将在禁用语言扩展打开时发出错误,并在 /W4 处发出警告。但是,删除此代码会破坏以前编译的代码,Microsoft 非常不愿意这样做。这也是他们不修复 SFINAE 支持的原因。

于 2011-08-25T11:50:34.637 回答
0

几年和许多版本的 Visual Studio 之后,我们仍然有这个“扩展”引起惊喜和头痛。叹...

解决方法是简单地将警告 C4239 转换为错误。这将阻止 MSVC 编译尝试将非 const 左值引用绑定到临时对象的代码,并为您提供清晰的编译器错误。只需添加/we4239到您的编译器定义或cl命令行参数。

在 Visual Studio 中:项目属性 > C/C++ > 所有选项 > 将特定警告视为错误 > 添加4239,并确保用分号分隔任何其他数字。

在 CMake 中:

if(MSVC)
    add_definitions("/we4239")
endif()

这似乎比使用官方不推荐的禁用所有语言扩展要好得多。/Za在我的大型代码库中,添加/Za导致来自 Microsoft 自己的winnt.h标头的 1500 多个编译器错误。

于 2019-07-10T21:03:47.547 回答
-1

这个问题有一个更糟糕的变体:

class Foo {
  int _val;
public:
  Foo(int v) : _val(v) {}
  void F() { std::cout << _val << std::endl; }
};

class Bar {
  Foo& f;
public:
  Bar(Foo& f) : f(f) {}
  void F() { f.F(); }
};

int main() {
  Bar b(Foo(3));
  b.F();
}

那么:b.f在调用 to 期间指向什么b.F()?上面的示例使用 VS2013 默认调试设置编译,运行时不会崩溃和打印3,但我怀疑任何更复杂的示例都会导致堆栈损坏。如果它没有并且编译器正在做一些“聪明”的事情来让它工作,那么我猜它真正在做什么是这样的:

class Foo {
  int _val;
public:
  Foo(int v) : _val(v) {}
  void F() { std::cout << _val << std::endl; }
};

class Bar {
  Foo f;
public:
  Bar(Foo&& f) : f(f) {}
  void F() { f.F(); }
};

int main() {
  Bar b(Foo(3));
  b.F();
}
于 2014-08-11T01:59:34.340 回答