3
char f1();
void f2(char&);

struct A {};

A    f3();
void f4(A&);

int main()
{
    f2(f1()); // error C2664. This is as expected.
    f4(f3()); // OK! Why???
}

error C2664: 'void f4(char &)' : cannot convert argument 1 from 'char' to 'char &'

I have been taught that in C++ a non-const reference parameter cannot be bound to a temporary object; and in the code above, f2(f1()); triggers an error as expected.

However, why does the same rule not apply to the code line f4(f3());?

PS: My compiler is VC++ 2013. Even if I comment the line f2(f1());, then the code containing f4(f3()); will be compiled without any errors or warnings.

Update:

MSDN says:

In previous releases of Visual C++, non-const references could be bound to temporary objects. Now, temporary objects can only be bound to const references.

So I think it is a bug of VC++. I have submitted a bug report to VC++ team

xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • Did you try building without f2(f1()) in the code? It may be stopping at the first error. – scaryrawr Oct 31 '13 at 02:49
  • If I comment the line `f2(f1());`, then the code will be OK. – xmllmx Oct 31 '13 at 02:50
  • MSVC is known for supporting this as compiler extension. Not sure about 2013. – yngccc Oct 31 '13 at 02:52
  • f1() should return a reference. – PersianGulf Oct 31 '13 at 02:53
  • @yngum, compiler extension? Could you refer me to the MSDN page? – xmllmx Oct 31 '13 at 02:54
  • @xmllmx just google `msvc const reference temporary`. – yngccc Oct 31 '13 at 02:58
  • @yngum, MSDN says: In previous releases of Visual C++, non-const references could be bound to temporary objects. Now, temporary objects can only be bound to const references. So I think this is a bug. – xmllmx Oct 31 '13 at 02:59
  • MS used to warn you about this and allow it anyway since their implementation did not efficiently unbind the reference as most compilers do. If they now flag it as an outright error, that's a *good* thing, because thats what it is by the standard. Their non-standard extension work on this regard was anything-but helpful, as it promoted non-portable code. – WhozCraig Oct 31 '13 at 03:10
  • @James, Could you move your comment to an answer? I think it is the correct answer. If you leave it a comment, then I cannot select it as an accepted answer. – xmllmx Oct 31 '13 at 03:15
  • @xmllmx: It is not a bug, but a compiler extension that can be disabled in compiler settings. By default extensions are enabled, meaning that be defualt the code will compile even in VS2012. You have to disable them explicitly. In all versions of MS compiler it is done by `/Za` switch – AnT stands with Russia Oct 31 '13 at 03:30
  • @AndreyT, According to http://msdn.microsoft.com/en-us/library/cfbk5ddc%28v=vs.90%29.aspx, it should be treated as a bug. – xmllmx Oct 31 '13 at 04:03
  • @xmllmx: I don't see what on that page made you think it should be treated as a bug. It was definitely a poorly implemented extension (a bug) in VC6, where it interfered with overload resolution, thus breaking conformant code. But by VS2005 it was reworked into a genuine extension. That's what it seems to remain to this day. What made you perceive it as a bug (as opposed to an extension)? – AnT stands with Russia Oct 31 '13 at 05:13
  • @AndreyT, If my compiler is pre-VC++ 2008, then this is not a bug. However, MSDN explicitly says: "Now, temporary objects can only be bound to const references.", and my compiler is VC++ 2013, so it is sure to be a bug. – xmllmx Oct 31 '13 at 09:10
  • @xmllmx: I'm not sure what you mean by that. The article you linked is written specifically for VS2003 (or VS2005, it is not really clear). However, in VS2003, VS2005, VS2010 and VS2012 the behavior remained unchanged: it is controlled by `/Za` flag. So, the MSDN article is either plainly wrong by making such "unconditional" assertion or we are simply missing the context for that article (presented elsewhere). I don't know how it works in VS2013, but I expect it to be the same. In any case, VS2013 is a completely different story, not related to that article. – AnT stands with Russia Oct 31 '13 at 14:22
  • In other words, if there's a "bug" here, it is a bug in the article you linked, not a bug in the compiler. The compiler works as intended. And the behavior is the modern versions of the compiler satisfies the requirements of a "language extension", meaning that it is not a bug. – AnT stands with Russia Oct 31 '13 at 14:25

3 Answers3

4

If you compile with the /Za option to disable language extensions, the compiler rejects both calls:

> cl /Za test.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

test.cpp
test.cpp(11): error C2664: 'void f2(char &)' : cannot convert argument 1 from 'char' to 'char &'
test.cpp(12): error C2664: 'void f4(A &)' : cannot convert argument 1 from 'A' to 'A &'
        A non-const reference may only be bound to an lvalue

There are several (very constrained) circumstances in which the compiler, with language extensions enabled, will still allow a non-const lvalue reference to bind to an rvalue expression. My understanding is that this is largely to avoid breaking several enormous legacy codebases that rely on this "extension."

(In general, use of /Za is not recommended for many reasons, but mostly because the Windows SDK headers cannot be #included with the /Za option.)

James McNellis
  • 348,265
  • 75
  • 913
  • 977
2

You compiler is not standard-compliant (maybe this is documented compiler extension?). GCC gives the following errors:

main.cpp: In function 'int main()':
main.cpp:11:11: error: invalid initialization of non-const reference of type 'char&' from an rvalue of type 'char'
    f2(f1()); // error C2664. This is as expected.
        ^
main.cpp:2:6: error: in passing argument 1 of 'void f2(char&)'
void f2(char&);
    ^
main.cpp:12:12: error: invalid initialization of non-const reference of type 'A&' from an rvalue of type 'A'
    f4(f3()); // OK! Why???
            ^
main.cpp:7:6: error: in passing argument 1 of 'void f4(A&)'
void f4(A&);
user12
  • 128
  • 2
  • 6
0

It appears that the compiler option /permissive- (Configuration Properties > C/C++ > All Options > Conformance Mode) also disables Microsoft language extensions (per James McNellis' answer) and will break non-conforming code.

This option disables permissive behaviors, and sets the /Zc compiler options for strict conformance.

https://learn.microsoft.com/en-us/cpp/build/reference/permissive-standards-conformance?view=msvc-160

For reference I was using Visual Studio 2019 Version 16.9.4 and compiling for the v142 platform toolset.