Compiler-generated copy/assignment functions for classes with reference and const members
Asked Answered
R

2

5

The book I'm reading says that when your class contains a member that's a reference or a const, using the compiler-generated copy constructor or assignment operators won't work. For instance,

#include <iostream>
#include <string>
using namespace std;

class TextBlock
{
    public:
        TextBlock (string str) : s(str) {
            cout << "Constructor is being called" << endl;
        }
        string& s;
};


int main () {
    TextBlock p("foo");
    TextBlock q(p);

    q = p;

    cout << "Q's s is " << q.s << endl;

    return(0);
}

According to my book, both the lines TextBlock q(p); and q = p; should return compiler errors. But using the g++ compiler for Linux, I'm only getting an error for the line q = p; When I comment that out, this works fine and the code compiles. The correct s is output for Q, so it's apparently being copied by the compiler-generated copy constructor. I get the same results when I change the line string& s; to const string s.

Have there been some changes to C++ that now allow the copy constructor to be generated automatically for reference and const objects, but not the assignment operator? Or maybe I'm just not understanding the book correctly? Any thoughts?

Ruttger answered 21/4, 2014 at 20:52 Comment(10)
Note that TextBlock p("foo"); is undefined behaviour. You end up with a dangling reference.Loud
@JamesKanze the member refererence is bound to the constructor's parameter taken by value.Moll
@JamesKanze There's a temporary string in the constructor, the reference is bound to that.Loud
@Loud Ah. I hadn't looked close enough. (I've never seen code which passed std::string by value. The usual rule is class types are always passed by reference to const.)Kile
@James: And that would still create a temporary, and still leave a dangling reference. (naturally it would only compile with const string& s)Pietism
Even if it was passed by constant reference, wouldn't that still be dangling, since that "foo" string will stop existing after the call to the constructor?Ruttger
@BenVoigt Yes. It's late. (Of course, about the only time it would make sense to have a std::string& as a member would be when it referred to an lvalue, with a std::string& argument to the constructor, so "foo" wouldn't compile. At least if you have a decent compiler.)Kile
I always thought g++ was fine, but it's compiling this with no errors or warnings (when I comment out the assignment).Ruttger
Which book is it that has such basic information wrong?Essary
The book I was going by was Effective C++ Third Edition by Scott Meyers. But it isn't the book's fault, it's mine. Reading it more carefully, the book doesn't say this is the case for the copy constructor, it was just talking about the copy constructor and then started talking about this problem with the assignment operator, and the way it was explained made me think it was talking about both, but reading it more carefully, it's really just talking about the assignment operator.Ruttger
K
4

The book is wrong. A const member or a reference member will inhibit generation of the default copy assignment operator, but doesn't prevent the compiler from generating a copy constructor.

Kile answered 21/4, 2014 at 20:54 Comment(3)
Thank you for your information. Can you explain the reasoning behind this/provide a source where I can read what the reasoning is? According to the book, neither make sense because it's undefined how reference or const members should be copied in copying situations. If that's not the case, I'd like to understand further.Ruttger
@user2242700: It's the same rule as for all other member and base subobjects -- the default copy constructor copies them using the copy-constructor corresponding to the member type. The copy-constructor on a const type works just fine. const objects can't be changed after construction, but const doesn't interfere with construction.Pietism
@BenVoigt And of course, the same thing holds for references. (For the rest, §12.8 explains all of the details, formally. But pratically, Ben's comment sums it up perfectly: if you can copy it, the compiler will generate a copy constructor.)Kile
P
3

Don't try to learn a special rule here.

The compiler-generated default versions of special member functions follow a simple pattern:

  • The type-appropriate special member function is called for every subobject (base classes and members).

From that, you can work out every case.

int i;
int &ri1 = i;
int &ri2 = ri1;

is allowed, so copying an object containing an int& is allowed.

There is no assignment operator for references (ri2 = ri1; does not rebind the reference), so assignment is not allowed.

References can't be default constructed:

int& ri; // error

so a type containing int& can't be default-constructed.

An important consideration is that the access checks are done for compiler-defaulted code just as if you had written it yourself. So interesting things can happen if a base class has, for example, a private copy constructor... and you don't have to learn special rules for any of them.

Pietism answered 21/4, 2014 at 21:13 Comment(1)
Thanks, that rule helps a lot.Ruttger

© 2022 - 2024 — McMap. All rights reserved.