Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behaviour of c++ constructor

Tags:

c++

c++11

I'm testing this code and wondering why this wasn't failed at compile time ?. I'm using c++11 and g++ 4.7.2.

I had similar structure on my production code, it was giving errors at run time, then I found that I'm constructing the class with wrong argument type.

#include <iostream>
#include <vector>


typedef std::vector<std::string> Word;

class Data {
    public:
        const Word &word;
        Data(Word w) : word(w) {}
};

class Base{
    const Data &data;
    public:
        Base(const Data &d): data(d) {}
        ~Base() {}
};

class Work : public Base{
    public:
        Work(const Data &d): Base(d){}
        ~Work() {}
};


int main(int argc, char **argv){
    Word words;
    words.push_back("work");

    /*
    * I'm confused with this constructor, why this passed the compilation
    * ??
    * Any special rule to reason this scenario ??
    *
    * But obviously it will fail at run time.
    */
    const Work *work  = new Work(words);

    return 0;
}
like image 638
Haridas N Avatar asked Sep 06 '13 07:09

Haridas N


2 Answers

Data is constructible from Word, so you can pass a Word to the Work constructor. Under the hood, an instance of Data will be created from the passed Word and, in turn, passed to the constructor.

You can avoid this by marking the constructor of Data that takes a Word as explicit, like this:

class Data {
    public:
        const Word &word;
        explicit Data(Word w) : word(w) {}
};

That way, the constructor cannot be implicitly applied anymore, and your call to the Work constructor will fail to compile unless you explicitly invoke the Data constructor:

const Work *work  = new Work(words);        // Implicit call, fails to compile.
const Work *work  = new Work(Data(words));  // Explicit call, compiles.
like image 179
Frédéric Hamidi Avatar answered Oct 13 '22 22:10

Frédéric Hamidi


It works compiles* because Data has an implicit conversion constructor that takes a Word reference:

Data(Word w) : word(w) {}

This means you can do things such as

Word words;
Data d1 = words;
Data d2(words);

and you can construct a Work from a Data via constructor Work(const Data &d): Base(d){}:

Work w(d2);

which means the following is also valid, because it only involves one user-defined conversion:

Work w2(words); // constructs Data temporary from words, then constructs w2 with it

This behaviour can be suppressed by declaring the converting constructor as explicit:

explicit Data(Word w) : word(w) {}

* Your doesn't actually work because it involves a dangling reference to a temporary Data object

like image 27
juanchopanza Avatar answered Oct 13 '22 21:10

juanchopanza