I wrote the following code to test this:
struct X
{
char* x;
X()
{
x = new char('a');
}
~X()
{
*x = 'b';
delete x;
}
};
void foo(const X& x)
{
}
void goo(X& x)
{
}
int main()
{
foo(X());
goo(X());
}
The destructors for the temporaries are called after both functions exit, but I thought you can only bind a temporary to a const reference. Why does goo work then?
Is it UB and MSVS is wrong, or is it ok?
It's illegal. A conforming implementation diagnoses it (i.e. it must at least warn), but MSVC++ allows it as an extension.
Or a bug if you're unkind, but IIRC the reason they still allow it is for long-lived legacy reasons: nominally to support code written for MSVC++ before C++ was standardized, but of course once you allow people to write this, they write it accidentally in new code too, so the legacy lives on. If it's deliberate then it's a (mis-)feature, not a bug, right? Anyway, a conforming implementation is required to diagnose ill-formed programs, so if you don't get a warning then the compiler isn't conforming.
This is apparently a MS extension. In GCC 4.3.4, for instance, it fails to compile, with the following message:
prog.cpp: In function ‘int main()’:
prog.cpp:25: error: invalid initialization of non-const reference of type ‘X&’ from a temporary of type ‘X’
prog.cpp:18: error: in passing argument 1 of ‘void goo(X&)’
Related
Running the code below in g++ will result the following error:
error: passing 'const A' as 'this' argument discards qualifiers [-fpermissive]
struct A{
void f(){};
};
int main(){
const A a;
a.f();
return 0;
}
Where do we use this?
fpermissive implies that the code is non-conformant.
Where does the standard states that this is an error?
The standard covers this in [class.this]. In particular, see example 2, where it says (in part):
The call y.g() is ill-formed because y is const and s::g() is a non-const member function.
When applied to your code, the equivalent statement is:
The call a.f() is ill-formed because a is const and A::f() is a non-const member function.
To be clearer: A::f() is non-const because it lacks a const qualification; whether or not f() would be a legal const function (if the const qualification was added) is not relevant.
Perhaps what you are really interested in, though, is the first part of [class.this], where it states that the keyword this has a meaning in every non-static member function, regardless of whether or not this is ever used in that function.
The following code does not compile on Visual C++ 2008 nor 2010:
#include <memory>
struct A {};
std::auto_ptr<A> foo() { return std::auto_ptr<A>(new A); }
const std::auto_ptr<A> bar() { return std::auto_ptr<A>(new A); }
int main()
{
const std::auto_ptr<A> & a = foo(); // most important const
const std::auto_ptr<A> & b = bar(); // error C2558:
// class 'std::auto_ptr<_Ty>' :
// no copy constructor available or copy
// constructor is declared 'explicit'
bar(); // No error?
}
I expected the "most important const" to apply to the variable "b", and yet, it does not compile, and for some reason, the compiler asks for a copy constructor (which surprises me as there should be no copy involved here). The standalone call to bar() works fine, which means, I guess, it is really the initialization of b that is the problem.
Is this a compiler bug, or a genuine compilation error described in the standard?
(perhaps it was forbidden in C++98 and authorized in C++11?)
Note: It does compile on Visual C++ 2012, gcc 4.6, and on Solaris CC (of all compilers...), but not gcc 3.4, nor XL C)
In C++03 and C++98, when binding a const reference to an rvalue (such as a function returning by value), the implementation may bind the reference directly to the rvalue or it may make a copy of the rvalue and bind the reference to that copy. As auto_ptr's copy constructor takes a non-const reference, this second choice will only work if the rvalue returned is not const qualified but the compiler is still allowed to attempt this, even if it won't work.
In C++11, these extra copies are not allowed and the implementation must bind directly to the rvalue if a conversion isn't required.
See also here.
Pre C++11, at least, the standard required an object to be
copyable in this context. In the end, the semantics of:
T const& t = f();
, where f returns a T by value, is:
T tmp = f();
T const& t = tmp;
Which requires a copy constructor.
In the case of std::auto_ptr, the problem that you're seeing
is that the copy constructor is defined to take a non-const
reference, which means that you cannot copy a temporary. Some
compilers (e.g. Microsoft) don't enforce this, which means that
your code may work with them, but it is fundamentally illegal.
The real question is why you are using references here. You
need a local variable one way or the other; the reference only
introduces an additional layer of indirection.
consider the below minimal example.
#include<iostream>
struct A
{
A(){std::cout<<"def"<<'\n';}
void foo()&{std::cout<<"called on lvalue"<<'\n';}
};
int main()
{
A a;
a.foo();
A().foo();
return 0;
}
this gives error about expecting ';' at the end of declaration and and expected un-qualified-id before '{'.
Can i know what i'm doing wrong? in the actual code i want to avoid calling the non-static member function through temporaries.
tried on gcc 4.7.2 and vc2010.
Thanks.
The answer will be short: VC10 and GCC 4.7.2 do not support ref-qualifiers.
However, notice that your foo() function has an lvalue ref qualifier, meaning that you cannot invoke it on temporaries.
If you want this expression to compile as well:
A().foo();
Then you should use const&, or provide an overload for &&, as in this live example.
To work with ref-qualifiers you can use Clang 3.2 or GCC 4.8.1.
Based on this
Incidentally, you can do this kind of assignment of a pointer to a
function as long as the target’s exception specification is no more
restrictive than the source’s
class A{};
class B{};
class C{};
void f() throw(A,B,C) {}
void (*pf)() throw(A,B);
int main()
{
pf = f; // pf is more restrictive than that of f. I expect an error here!
}
The last statement should not pass the compiler. However, I have tried VS2010 and GCC latest version. Neither or them complain about it.
Question> Why?
This looks like a bug in GCC and VS2010. Clang won't compile it, and it should not compile according to [except.spec]/5:
... A similar restriction applies to assignment to and initialization of pointers to functions, pointers to member functions, and references to functions: the target entity shall allow at least the exceptions allowed by the source value in the assignment or initialization.
The standard also includes the following example:
class A { /*...*/ };
void (*pf1)(); // no exception specification
void (*pf2)() throw(A);
void f() {
pf1 = pf2; // OK: pf1 is less restrictive
pf2 = pf1; // error: pf2 is more restrictive
}
It's a long-standing bug in g++: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12255
The underlying issue is that G++ just ignores exception-specifications on
non-function declarations. I think this choice originated in the uncertainty
about whether or not the exeception-specification would become part of a
function type rather than remain associated with the declaration.
It wouldn't surprise me if VS was similar in this regard.
The following is a common typo with language newcomers, who think that they are defining an object but are actually declaring a function:
struct T
{
void foo() {}
};
int main()
{
T obj();
obj.foo();
}
GCC 4.1.2's error is:
In function 'int main()':
Line 9: error: request for member 'foo' in 'obj', which is of non-class type 'T ()()'
compilation terminated due to -Wfatal-errors.
Why is the reported type in the message T ()()? I'd have expected T ().
IIRC this is just a compiler bug. GCC 4.4 says T() while 4.2 says T()() for me.
The error is best understood when you realize that you usually don't write out function types without naming at least the function, but it's a bit more common for function pointers.
For instance, int (*fooPtr)() names the pointer. If you omit the name, you have int (*)(). Now, going from function pointer to function type would give you int ()().
There's no real standard here, because ISO C++ doesn't define canonical names for all types. For instance, const volatile int is the same type as volatile const int, and neither form is canonical.