2

I have a struct A that has several constructors with different data members initialized.

template<typename T>
    struct A {

    typedef std::vector<T> type1
    type1 a;
    type1 b;
    type1 c;

    A(type1 i_a): a(i_a) {
    }

    A(type1 i_a, type1 i_b): A(i_a), b(i_b) {
    }

    A(type1 i_a, type1 i_b, type1 i_c): A(i_a, i_b), c(i_c) {
    }
};

Error I get is when I instantiate it with say custom_type, the error is type A<custom_type> is not direct base of A<custom_type> highlighting the constructor I call within another constructor. I am using C++11. What's the problem?

ThinkingStiff
  • 64,767
  • 30
  • 146
  • 239
user592748
  • 1,194
  • 3
  • 21
  • 45
  • What compiler and options? It looks like you aren't using C++11, even if you intended to. – Ben Voigt Mar 15 '13 at 17:40
  • It could be that your compiler does not support delegating constructors. Which compiler and version are you using? – juanchopanza Mar 15 '13 at 17:41
  • I use `g++ -std=c++0x` if that is what you mean? And the version is 4.6 – user592748 Mar 15 '13 at 17:44
  • I'm not sure it's allowed to combine calls to other constructors with direct initialization of members - it works if you remove the init of `b` and `c` – SomeWittyUsername Mar 15 '13 at 17:45
  • possible duplicate of [Member initialization while using delegated constructor](http://stackoverflow.com/questions/12190051/member-initialization-while-using-delegated-constructor) – SomeWittyUsername Mar 15 '13 at 17:51

3 Answers3

7

A constructor may initialize its base classes and members, OR delegate to another constructor of the same class, not both.

A(i_a) constructs a complete A object, copying member a and default-constructing b and c. So it doesn't make sense to have A(type1 i_a, type1 i_b): A(i_a), b(i_b) {} - the first initializer has already initialized b. You could instead have

A(type1 i_a, type1 i_b) : A(i_a) { b = std::move(i_b); }
aschepler
  • 70,891
  • 9
  • 107
  • 161
  • 3
    That's it: "If a mem-initializer-id designates the constructor's class, it shall be the only mem-initializer; the constructor is a delegating constructor, and the constructor selected by the mem-initializer is the target constructor." from 15.6.2p6 – Ben Voigt Mar 15 '13 at 17:44
  • And this problem is why it's generally best to have constructors with fewer parameters delegate to constructors with more; delegate everything to the most general constructor and specialized constructors will do their job by simply sending default values to the more general constructor. – bames53 Mar 15 '13 at 18:17
  • I'm not sure about the statement "*So it doesn't make sense to have* `A(type1 i_a, type1 i_b): A(i_a), b(i_b) {}` *- the first initializer has already initialized* `b`". Sounds like a reasonable situation to initialize an object and overwrite `b` value after that. Whether or not it's problematic as far as C++ standard goes, it's another question – SomeWittyUsername Mar 15 '13 at 18:24
  • 1
    @icepack: Mem-initializers result in calling a constructor. Only one constructor is called per (sub)object. Overwriting would be an assignment, not an initialization. – aschepler Mar 15 '13 at 18:28
  • I know that, I'm just saying it's not an unreasonable request to enable such operation in the language - for example, if mem-initializer follows constructor call, the compiler can omit the initialization of this field and delay it till mem-initializer invocation. – SomeWittyUsername Mar 15 '13 at 19:01
  • @icepack That would have far reaching consequences. E.g. currently sub-objects are constructed and legal to use before any of an object's constructor bodies run, but your suggestion would change that; Constructors could no longer expect sub-objects to be in usable states. A better solution is just to have delegation happen in the direction opposite to what's shown by the OP. – bames53 Mar 15 '13 at 19:49
1

aschepler provides the answer but I wanted to explain what lead to the problem and show 'best practices' for delegating constructors to avoid it.

In your code you have a series of constructors, each more general than constructor they delegate to. That is you're trying to have a constructor delegate to a more specialized constructor that doesn't do everything and then you tack on a bit of work to handle the extra generality. This is backwards from what has turned out to be the most practical way of construction delegation in earlier languages that support it.

What you want instead is to have the most general constructor be the 'root' of the delegation (the 'designated initializer' or 'designated constructor', in other languages). More specialized constructors will do their work using more general constructors, passing them the data for the special case to be handled.

In your case the specialized behavior is to use default values for some members instead of taking initial values from the user. So your more specialized constructors will do their work by passing on the parameter they're given along with default values for the other members.

template<typename T>
struct A
{
    typedef std::vector<T> type1;
    type1 a;
    type1 b;
    type1 c;

    A(type1 i_a, type1 i_b, type1 i_c): a(i_a), b(i_b), c(i_c) {}

    A(type1 i_a, type1 i_b): A(i_a, i_b, {}) {}

    A(type1 i_a): A(i_a, {}) {}
};
bames53
  • 86,085
  • 15
  • 179
  • 244
0

but you can still call different constructors of the same class from the body of constructor (if for some reason you would want to)

class CComplex{
public:
    CComplex(int real1,int image1,char c)
    {
        cout<<"RealImg";
        real=real1;
        image=image1;
        char* x; char xc=c;x=&xc;
        void* v;
        f(x);
        CComplex ccc(x,v); //this is OK
        CComplex cccc(1,2,3); //as this too
    }
    CComplex():real(0),image(0){cout<<"DEFAULT";}
    CComplex(const CComplex &c)
    {
        real=c.real;
        image=c.image;
        cout<<"COPY";
    }
    CComplex& operator=(CComplex const& ref){ 
        cout<<"ASSIGN";
        //CComplex* c;
        CComplex cobj(43,45,'x');
        //c=&cobj;
        //CComplex* c=new CComplex(44,45); 
        return cobj;
    }

    CComplex(int i1, int i2, int i3){cout<<"\n123!";}
    CComplex(const char* y,void* v){cout<<"\nCrefvoid!";}
    ~CComplex(){cout<<"\n~CComplex(){}";}
public:
    void Display(void)
    {
        cout<<real<<"+"<<image<<"i"<<endl;
    }
    static bool CComplexComparator(CComplex c1, CComplex c2){return true;}
    static void CComplexInit(CComplex& c){
        c.real=100;
    }
    int real,image;
}; 
4pie0
  • 29,204
  • 9
  • 82
  • 118
  • The statements declaring `ccc` and `cccc` wouldn't be able to help initialize the original `CComplex` object like a delegating constructor would, of course. – aschepler Mar 15 '13 at 18:18
  • how does this relate to the question?.. In any case, this isn't different from calling another version of overloaded function from inside the function (or even the same one) – SomeWittyUsername Mar 15 '13 at 18:21
  • I wanted to show that it is still possible to call different version of constructor. possible – 4pie0 Mar 15 '13 at 18:28
  • These are fundamentally different. Your internal constructor call relates to entirely different object. – SomeWittyUsername Mar 15 '13 at 19:03