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();
}
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
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
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
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.
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.
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.
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();
}
Related
A colleague asked me to look over some code he was writing and came across a problem with a particular line, which the compiler (g++) would complain about a function call not having a matching function based on its parameters.
After solving the problem in two ways (one by moving the parameter to its own variable and passing that instead, next is changing the parameter list to take it as a const reference), I had to ask this question: Why is the solution the way it is? I wasn't satisfied with writing it off as if some constructor details were being hidden away, as my colleague puts it.
As a result, I've replicated and reduced the problem to the following (compile with g++ -Wall -ansi -pedantic):
class SomeClass
{
public:
static void SomeFunction(SomeClass& sc) {}
static void SomeFunction2(const SomeClass& sc) {}
};
class SomeChild : public SomeClass {};
void testOne(void)
{
// this compiles
SomeChild sc = SomeChild();
SomeClass::SomeFunction(sc);
// this doesn't compile
//SomeClass::SomeFunction(SomeChild());
}
void testTwo(void)
{
// this compiles
SomeChild sc = SomeChild();
SomeClass::SomeFunction2(sc);
// this compiles
SomeClass::SomeFunction2(SomeChild());
}
int main(void)
{
testOne();
testTwo();
return 0;
}
I'm probably missing something very fundamental here, but can anyone explain to me why the compiler thinks there is no matching function call for the uncompilable line?
Thanks in advance.
The simple reason is that temporary values, such as the value of SomeChild(), cannot bind to non-constant lvalue references. While there's no deep technical reason for this, it's a design choice: non-constant references are usually used to modify the thing that's being referred to, and if that thing is temporary, then the modification would essentially have no lasting effect, which is almost always logic error.
Just replace 'SomeFunction2' by 'SomeFunction':
#include <iostream>
class SomeClass
{
public:
static void SomeFunction(SomeClass& sc) { std::cout << "not const" << std::endl; }
static void SomeFunction(const SomeClass& sc) { std::cout << "const" << std::endl; }
};
class SomeChild : public SomeClass {};
int main(void)
{
SomeChild sc = SomeChild();
SomeClass::SomeFunction(sc);
SomeClass::SomeFunction(SomeChild());
return 0;
}
And all is good.
Without the change you bind a temporary to a reference, that is impossible. The only alternatives are 'T', 'const T&' or a universal reference 'T&&' ( http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers)
For this code
struct test {};
test f() { return test(); }
void print(test *x) {}
int main()
{
print(&f());
print(&test());
}
gcc-4.6 emits two "taking address of temporary [-fpermissive]" errors. This was introduced in 4.6, gcc-4.5 could compile it.
The reason is pretty clear and well documented. The problem is that it is a legacy code and, to compile, we have to make it work, thus, doing #pragmas around files and/or parts of code to compile them with -fpermissive. Let's say, customers are adamant not to modify the existing code (i.e. the fact of calling print() with &f() or &test() cannot be changed, not source files in general). In other words, one way or another this will be compiled, the only choice is more or less pain.
So the question is - are there any possible workarounds to make it work without doing -fpermissive in lots of places? -W flags, C++ tricks, etc.
What I meant is, the fact of calling print() with &f() or &test() cannot be changed.
If you have control over the type itself, you can always overload the reference operator, operator&, and return this from it. It's not a good thing to do in the general case, but it's fairly safe considering that you're returning the correct pointer of the correct type.
If base classes are involved, then it becomes rather more complicated. You'll need to use a virtual operator overload, and each class in the hierarchy will need to implement it separately.
You can provide a workaround by creating an extra print overload that will take a const&:
void print( test const & t ) {
print(&t); // Assuming that the function is 'print( test const * )'
// a print function should not take a non-const pointer!
}
And changing the caller to:
print( f() );
This requires code changes, but in a very limited form, so it might be acceptable. Also note that
Why not just rewrite the code to do exactly what the compiler used to do without complaining? In other words, store your temporary in a temporary (but addressable) variable.
struct test {};
test f() { return test(); }
void print(test *x) {}
int main()
{
test t1 = f();
test t2 = test();
print(&t1);
print(&t2);
}
This should behave identically to the way it did with the old compiler version. Tell the customer that the new compiler requires you to change the code to be explicit about something the compiler used to do implicitly.
It is not clear to me how much control you have over what, but the following hack seems to work:
Edit: Originally had f return a reference instead of a copy. DeadMG correctly points out this leads to undefined behavior as soon as the temporary gets used, so restored f back to returning a copy.
struct test {};
const test & test_copy (const test &t) { return t; }
#define test() test_copy(test())
test f() { return test(); }
#define f() test_copy(f())
void print(const test *) {}
int main()
{
print(&f());
print(&test());
}
The hack is to basically convert your temporaries into const references, so that the compiler will be happier. It shouldn't be made generally available, it's only purpose is to shut the compiler up. But, it doesn't really solve any underlying problems with the customer code or provided API. You could fix your API, and only resort to the hacks for certain customers:
struct test {};
test f() { return test(); }
void print(const test &) {}
#define CUSTOMER_NEEDS_HACK
#ifdef CUSTOMER_NEEDS_HACK
const test & test_copy (const test &t) { return t; }
#define test() test_copy(test())
#define f() test_copy(f())
void print(const test *t) { print(*t); }
#endif
I'm trying to compile the code in Visual Studio (2008) and g++.
In vs2008 it's successful, but in g++ it reported error.
if add const,
test(const test &source):a(source.a) {}
g++ will compiled succeed.
I kown that test aa = 2; will create a temporary object and call copy-constructor.
temporary object cannot bind to a non-const reference
so,why the vs2008 can compiled it succeed?
class test{
public:
test():a(1) {}
test(int num):a(num) {}
test(test &source):a(source.a) {}
private:
int a;
};
int main(){
test aa = 2;
return 0;
}
VS has a non-standard extension that allows it, unfortunately.
There's a compiler flag to disable extensions, but last I checked it also makes it impossible to use the standard library. Your best bet is to keep the Warning Level on 4 (though this particular situation gets no warning), and check your compilations with multiple compilers when possible.
the following code compiles with Visual Studio 2008 but not with g++ on Mac OSX:
class A
{
public:
A CreateA()
{
A a;
return a;
}
};
class B
{
public:
void ComputeWithA
(
A &a // Here, removing the reference solves the problem
)
{
return;
}
};
int main ()
{
A a;
B b;
b.ComputeWithA(a); // Works
b.ComputeWithA(a.CreateA()); // Invalid arguments 'Candidates are: void ComputeWithA(A &)'
return 0;
}
Why this reference related issue? Any explanation would be greatly appreciated.
Best regards.
a.CreateA() gives you a R-Value (i.e. a temporary). ComputeWithA wants a reference, aka L-Value. The standard says that you can't convert R- into L-Values, so the MSVC is wrong here.
However, you can take a const reference since this case is explicitly allowed:
void ComputeWithA(A const &a) // add a const and everything works fine
The code isn't legal but VC is accepting it. Your function returns a temporary, which cannot be bound to a non-const reference (which your function takes as its argument).
Can you change your function to take its parameter by const reference?
If you can't change the signature of the method, there is a workaround
class A
{
public:
A& operator()()
{
return *this;
}
...
b.ComputeWithA(a.CreateA()());
I've used this once when implementing a logger, because the temporary object was used in more operations.
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();
}
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
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
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
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.
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.
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.
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();
}