1

Its unclear to me whether a temporary assumes type of const or not, in an expression as shown below.

#include <iostream>

class X {
public:
X(int a) { i = a; cout << "X(int) [" << (int)this << "]" << endl; }

X& operator+(const X& x) 
{ 
i += x.i; 
cout << "X operator+(const X&) [" << (int)this << "]" << endl; 
return *this; 
}

~X() { cout << "~X [" << (int)this << "]" << endl; }

private:
int i;
};


int main() 
{
X x = X(3) + X(4);
cout << "done" << endl;

return 0;
}

X(3) behaves like non-const (because I can call operator+, while X(4) behaves like const(because, it needs const argument in operator+).

Can someone clarify, what is the right understanding?

Chethan
  • 905
  • 1
  • 10
  • 18

4 Answers4

2

You can call non-const members of temporaries. But you cannot bind a non-const reference to a temporary.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • Just a nit, but that should be "you cannot initialize a non-const reference with an rvalue expression". It's very possible to obtain non-const references bound to a temporary. (Try `std::ostream& s = std::ostringstream().flush();` for example.) – James Kanze Nov 16 '11 at 10:06
2

When it comes to class types, when you create a temporary of const type, that temporary will be const. And when you create a temporary of non-const type, that temporary will be non-const. That's it. I.e. as far as the exact type is concerned, there no connection between const and temporaries at all. Temporary of class type never assumes const by itself. It is you who can explicitly impose const on it.

In your example nether X(3) nor X(4) is const. Since X(3) is not const, you can call a non-const method on it.

It is not correct to say that your X(4) "behaves as const". There's no indication that it "behaves as const" in your example whatsoever. Just because you were able to initialize a const reference with something does no mean that that something is const.

In your question you state that you "need const argument in operator+". That's incorrect. You don't need const argument in your operator+. Your parameter x is declared as const reference. A const reference can be easily bound to const arguments as well as to non-const arguments. In your case const reference parameter x is bound to a non-const temporary argument X(4).

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • But as i mentioned, operator+ requires a *const* X& argument for this to compile. If i just have X& operator+(X& x), the compilation fails. – Chethan Nov 16 '11 at 09:18
  • 1
    @Chethan: That's caused by the properties of references, not by properties of temporaries. In C++ only const references can be bound to temporaries. Non-const references cannot be bound to temporaries. Whether the temporary itself is const or not does not matter at all. – AnT stands with Russia Nov 16 '11 at 09:20
  • @Oli Charlesworth: That's really about references, not about temporaries. – AnT stands with Russia Nov 16 '11 at 09:20
2

Historically, temporaries are rvalues, and rvalues aren't (and can't be) cv-qualified. This rule worked well for non-class types, or for class types with no member functions; since const-ness intervenes in function overload resolution, the cv-qualification of a temporary must be maintained. If a function returns simply X, then the temporary is not const, and you can call non-const functions on it; if the functions returns X const, then the temporary is const, and you can't call non-const functions on it. As a general rule, it's probably preferable to return class types as const; i.e. X const f(), rather than X f(). But there are definitely exceptions, and no body does it, even in the cases where it would be more appropriate. And finally, there are contexts where you can't specify the const-ness—function style type conversions, for example—where the syntax doesn't provide a means of specifying cv-qualifiers (except by using typedef's).

You might want to look at the output from the following code:

class C
{
    std::string myComment;
public:
    C( std::string const& comment ) : myComment( comment ) {}

    void f()
    {
        std::cout << "Non const " << myComment << std::endl;
    }

    void f() const
    {
        std::cout << "Const " << myComment << std::endl;
    }
};

typedef C const CC;

C
c()
{
    return C("function return value");
}

C const
cc()
{
    return C("function return value");
}

int
main()
{
    C("function style conversion").f();
    CC("function style conversion").f();
    c().f();
    cc().f();
}
James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • I think the advice to return class-types as const is positively obsolete now. In C++11 this interferes heavily with move semantics, which only work for non-const rvalues. – Kerrek SB Nov 16 '11 at 13:47
  • @KerrekSB I don't know. I suspect that it depends on the application; returning const will certainly cause some errors to be caught by the compiler, which can be a strong argument. On the other hand, almost nobody does it, so the errors it would catch can't be that frequent. As for move semantics: how many classes actually support them? I'm not sure that they're that important. (But again, it depends on the application.) – James Kanze Nov 16 '11 at 15:46
  • Hm... `string`. `vector`. Any standard library container. Iostreams? Unique pointers. Locks. Hmm... – Kerrek SB Nov 16 '11 at 15:50
1

In the following code:

X x = X(3) + X(4);

X(3) creates a temporary non-const object which invokes operator+, passing X(4) which is another temporary non-const object as const reference argument to the function.

A non-const object can be passed to a function as const object (passing by reference), but a const object cannot be passed by non-const reference to a function. That is the difference.

Nawaz
  • 353,942
  • 115
  • 666
  • 851