kesarling
kesarling

Reputation: 2260

Why is accessing the members of a member object not allowed in a ctor?

class Edge:

class Edge {
    int dist = 0;
    std::pair<Node, Node> ends;
public:
    Edge() = default;
    explicit Edge(const int idist) : dist(idist) { }
    explicit Edge(const int idist, Node& end1, Node& end2) : dist(idist) {
        ends.first = end1;
        ends.second = end2;
    }
    ~Edge() = default;
};

In the ctor explicit Edge(const int idist, Node& end1, Node& end2), why am I not allowed to use the syntax?:

explicit Edge(const int idist, Node& end1, Node& end2) : dist(idist), ends.first(end1), ends.second(end2) { }

Upvotes: 1

Views: 66

Answers (3)

Pete Becker
Pete Becker

Reputation: 76523

One of the fundamental features of C++ classes is that every object (absent malice) will be properly created by its constructor. Looked at the other way around, if a type has a constructor, then you must create objects of that type with the constructor.

In the constructor initializer list for Edge, each member of Edge gets constructed. Since std::pair has its own constructor, constructing an Edge object means constructing that ends member with the appropriate constructor for std::pair.

That's not something you should be trying to bypass. It's fundamental to sound programming. Constructors create objects. Doing your own thing risks creating things that haven't been properly created. Yes, in this particular case, you can probably get away with it. But there's nothing to gain from bypassing the constructor, and in other cases, that would cause significant problems. Don't bypass constructors. Let them do their job, so your job will be much easier.

Upvotes: 0

songyuanyao
songyuanyao

Reputation: 173044

This is just not allowed. As the syntax of member initializer list,

class-or-identifier ( expression-list(optional) ) (1) 
class-or-identifier brace-init-list   (2) (since C++11)

While ends.first and ends.second don't refer to class or identifier, they're expressions. You have to initialize ends in whole, e.g.

explicit Edge(const int idist, Node& end1, Node& end2) : dist(idist), ends(end1, end2) { }

Upvotes: 4

Lytigas
Lytigas

Reputation: 944

What you're trying to do is allowed, but the way you're doing it isnt.

The initializer list specifies how to turn a random bit of memory containing uninitialized bytes into a valid object. The constructor can then do things with this new object, but the object state needs to be initialized beforehand. There are some caveats to this surrounding uninitialized values resulting from default initialization, but the only valid thing to do in an initializer list is call a member's constructor. (See here for more on default initialization.)

In your case, first and second are fields of a pair. You can't access them until the pair has been constructed. And even then, you couldn't necessarily re-initialize them the way you're attempting to.

The solution is to initialize the whole pair at once using one of its constructors:

explicit Edge(const int idist, Node& end1, Node& end2) : dist(idist), ends(end1, end2) { }

Upvotes: 1

Related Questions