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 is the good one to give a compile error: invalid initialization of non-const reference of type std::string& from a temporary of type std::string
  2. VS2008 is not too bad as at least it gives a compile warning: warning C4239: nonstandard extension used : 'initializing' : conversion from std::string to std::string & A non-const reference may only be bound to an lvalue
  3. Here comes the problematic one - VS2010(SP1) comples fine WITHOUT any error or warning, WHY ??!! I know rvalue reference in VS2010 can be used to bind with rvalue but I am NOT using &&, instead in the demo code, I was just using non-const lvalue reference !

Can somone help me explain the behavior of VS2010 here? Is it a bug !? Thanks

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
RoundPi
  • 5,819
  • 7
  • 49
  • 75
  • 1
    what are your warning settings ? Perhaps that it is your configuration which changed, and not their implementation ? – Matthieu M. Aug 25 '11 at 11:45
  • I have been using VS2008 for a while but recently installed VS2010 so VS10 it's should be using it's default setting... – RoundPi Aug 25 '11 at 13:08
  • 2
    then I am afraid that the warning treshold is simply not high enough. Use `\W4` to activate warnings in the `4xxx` range. – Matthieu M. Aug 25 '11 at 13:13
  • @Gob00st - So the complaint is that the deafult settings aren't the ones you need? – Bo Persson Aug 25 '11 at 13:36
  • @Bo Persson - I am not happy about the default setting for VS2010 which doesn't give any error or warning ! VS2008 did give a warning which is better than nothing IMHO. – RoundPi Aug 25 '11 at 13:48

4 Answers4

12

That is a known issue/feature of the VS compilers. They have always allowed that and there does not seem to be any push into removing that extension.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Any idea why MS allowed this illegal c++ usage in the first place ? This particular reference rule should be quite old(98 or 03 standard)? – RoundPi Aug 25 '11 at 13:10
  • @Gob00st: this is an extension that circumvents the arbitrary C++ restriction. I am personally of mind that binding should be either allowed or disallowed and that the current *half-allowed* is really an awkward state. Disallowed entirely would eliminate a whole class of bugs, even though it would be painful, Allowed entirely would straighten the specs. – Matthieu M. Aug 25 '11 at 13:15
  • 2
    @Gob00st - MS has some old code that was written before the rules were fixed (and so might their customers have). They believe it is more important to support old code than new standards. It is also possible to turn the extensions off (and thus being unable to compile some Windows headers). – Bo Persson Aug 25 '11 at 13:34
9

The compiler will issue an error with Disable Language Extensions turned on, and a warning at /W4. However, removing this code will break previously compiling code, and Microsoft is very reluctant to do that. This is also why they won't fix their SFINAE support.

Puppy
  • 144,682
  • 38
  • 256
  • 465
0

Several years and many versions of Visual Studio later, we still have this "extension" causing surprises and headaches. Sigh...

The fix is to simply turn warning C4239 into an error. This will prevent MSVC from compiling code that attempts to bind a non-const lvalue reference to a temporary, and give you a nice clear compiler error. Simply add /we4239 to your compiler definitions or cl command line arguments.

In Visual Studio: Project Properties > C/C++ > All Options > Treat Specific Warnings As Errors > add 4239, and make sure to separate any other numbers with a semicolon.

In CMake:

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

This seems to work far better than disabling all language extensions with /Za, which officially not recommend. On my large code base, adding /Za caused over 1500 compiler errors from Microsofts's own winnt.h header.

alter_igel
  • 6,899
  • 3
  • 21
  • 40
-1

There is a much nastier variant of this problem:

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();
}

So: to what does b.f point during the call to b.F()? The above example, compiled with VS2013 default Debug settings, runs without crashing and prints 3, but I'd suspect that any much more complex example will lead to stack corruption. If it doesn't and the compiler is doing something 'clever' to make it work, then I guess what it is really doing is this:

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();
}
Tom
  • 7,269
  • 1
  • 42
  • 69