tags:

views:

384

answers:

3

Would it be correct to say that whenever casting is used, the resulting object is a const object?

...And therefore can only be used as an argument to a function if that function accepts it as a const object?

e.g.

class C1 {
    public: C1(int i=7, double d = 2.5){};
};

void f(C1& c) {};

int main(){
    f(8);
    return 1;
}
//won't compile

(Of course if f(....) receives the argument by value then it gets a non-const version which it can work with)

+12  A: 

Usually when an object of a certain type is converted to an object of another type (a non-reference type), a temporary object is created (not a const object). A temporary object (invisible, nameless, rvalue) can only bind (in C++98/03) to references that are const (except for the temporary known as the 'exception object'). Therefore, the result of a conversion that creates a temporary can only be used as an argument to a function that either accepts it as a const reference, or as a type that it can be copied/converted into.

So for e.g.

void f(double);  // 1
void f(const int&); // 2
void f(int&); // 3

struct B {  B(int) { } };
struct C { C(int)  { } };
void f(B b);  // 4
void f(B& b); // 5
void f(const C& c); //6
void f(C& c); // 7

// D inherits from B
struct D : B { D(int) : B(0) { } };
void f(D& d); // 8

int main()
{
   f( (int) 3.0 ); // calls 2, NOT 3
   f( (float) 3 ); // calls 1 - const reference requires the same type
   f( 1 );  // calls 2, NOT 3

   f( (B) 1 ); // calls 4, not 5 - can accept it by non-const value
   f( (C) 1 ); // calls 6, not 7 - can accept it by const-reference

   f( (D) 1 ); // calls 4, not 8 - since a 'D' temporary object can be converted to a 'B' object - but will not bind to a non-const reference
}

Hope that helps.

Faisal Vali
+1: I like this answer. Tho it may be useful to give the ranks and kind of each conversions (to show why a particular version is selected over another). Please check that I'm right. I enumerate A, B, C, .... -- First call (A): exact match to 2, conversion to 1. (B) promotion to 1, conversion to 2. (C) exact match to 2, conversion to 1. (D) exact match to 4. (E) exact match to 6. (F) Conversion (derived->base) to 4. -- In all the reference bindings of `const T void f(const T f(T());` ambiguous)
Johannes Schaub - litb
yes i think you're right in your break down of the conversions and ranking - since we covered OR here http://stackoverflow.com/questions/1092714/conversion-precedence-in-c/1092724#1092724, i didn't want to focus too much on Overload Resolution - but more so on what a temporary could bind to - perhaps i should have used different function names and kept it simpler?
Faisal Vali
Oh i see. I don't know what would be better. But i like it how it is currently. People could read your other answer and look into the comment section if they wanna know why some version is preferred in particular.
Johannes Schaub - litb
A: 

You can rewrite that as:

class C1
{
    public: C1(int i=7, double d = 2.5){};

};

void f(C1& c){};

int main()
{
    C1 c(8); //named
    f(c);
    //... can access the modified C1 instance here ...
    return 1;
}

The thing that the compiler doesn't like about your version is that your C1 instance is an un-named temporary: which f will modify, but (because it's an un-named temporary) the compiler knows that the caller (main) has no way to receive/preserve/notice those modifications.

ChrisW
+3  A: 

It's a little off to call this 'casting'. You are doing 'implicit construction'. Implicit construction always creates a temporary object, which in turn is always const.

Note you can mark the constructor 'explicit' and this would block the implicit construction call you're seeing here. But that only means you would need f(C(8)) instead of f(8); the C instance would still be const because it is temporary, however note no cast is being done.

See also const_cast for dirty workarounds if you really, really need to. The other workaround would be to do a two liner: C c(8); f(c);

Ethan