Is it possible to delay base class ctor call in member initializer list?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream> 

struct Sample
{
    int x = 0;
    Sample() {}
    Sample(int value1) : x(value1) {}
};

struct DSample : public Sample
{
    int y = -100;
    DSample(int value2) :
       // Work needs to be done on value2 before the result can be passed to the base class for initialization 
       Sample(value2 * 2) 
    {
    }  
};

int SomeWorkOnValue2(int expectedValue2) 
{
    // Do stuff with value2
    return = expectedValue2 * 2; 
}

int main() 
{
   Dsample ds = DSample(100);
}


Suppose you want to do something with value2 before you invoke the base class ctor in the mem-initializer list.

DSample(int value2) : Sample(value2 * 2)

How is this done? One way I can think of is to call a function:

DSample(int value2) : Sample(SomeWorkOnValue2(value2))

Of course, you can just let the base class be default-initialized and do work to re-assign to base class data members:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Sample
{
    int x = 0;
    Sample() {}
    Sample(int value1) : x(value1) {}
};

struct DSample : public Sample
{
    int y = -100;
    DSample(int value2) : Sample() // Let base class default-initialize
    {
        // Re-assign to base class values 
        Sample::x = SomeWorkOnValue2(value2); 
    }  
};


But re-assignment can be ugly if the base class member initialization is complicated. It also puts the responsibility of base class construction in the derived class' ctor, which is generally not a good implementation decision.
Last edited on
> One way I can think of is to call a function.

Yes, use a helper function (or any expression which yields the desired value).

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <string>

struct base
{
    explicit base( std::string str = {} ) noexcept : text( std::move(str) ) {}
    std::string text ;
};

std::string trim( std::string str )
{
    std::size_t pos = 0 ;
    while( pos < str.size() && str[pos] == ' ' ) ++pos ;
    while( !str.empty() && str.back() == ' ' ) str.pop_back() ;
    return str.substr(pos) ;
}

struct derived : base
{
    explicit derived( const std::string& str ) : base( '"' + trim(str) + '"' ) {}
};

http://coliru.stacked-crooked.com/a/248d510b18d332a8
ElusiveTau wrote:
Is it possible to delay base class ctor call in member initializer list?

No.

ElusiveTau wrote:
Suppose you want to do something with value2 before you invoke the base class ctor in the mem-initializer list.
DSample(int value2) : Sample(value2 * 2)
How is this done?

If it's just as simple as multiplying by 2 I don't see why you wouldn't want to do it this way.

ElusiveTau wrote:
One way I can think of is to call a function:
DSample(int value2) : Sample(SomeWorkOnValue2(value2))

If the calculation is complicated so that you cannot easily write it as a simple expression then a function call seems like a good idea.

ElusiveTau wrote:
Of course, you can just let the base class be default-initialized and do work to re-assign to base class data members:

This might be necessary if you need to do calculations that depend on multiple values.

I agree that the member initialization list should be preferred but if writing it in the constructor body is necessary or easier I don't think it's wrong doing so. Just do what you have to do to initialize the object.

ElusiveTau wrote:
But re-assignment can be ugly if the base class member initialization is complicated.

Ugly as in non-optimal performance wise?

If the default constructor is cheap and does minimal amount of work then calling it would normally not be a problem. What I mean is for example if you're writing a std::vector-like class then don't make the default constructor allocate an array. Just set the size and capacity to 0 and the data pointer to null.

If it really is a problem you could always work around it by providing a protected constructor that does no or minimal work that you can use from the derived class and possibly also an init function that you can call. That way you're essentially delaying the base class initialization like you said but it requires that you adjust the base class to do this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Sample
{
public:
	Sample() { init(0); }
	Sample(int value1) { init(value1); }
protected:
	void init(int value1) { x = value1; };
	enum class NoInit {};
	Sample(NoInit) { }
private:
	int x;
};

class DSample : public Sample
{
public:
	DSample(int value2) : Sample(NoInit{})
	{
		init(SomeWorkOnValue2(value2));
	}
};
Last edited on
Peter87 wrote:
This might be necessary if you need to do calculations that depend on multiple values.

Hm. You're right. The derived class' ctor might first need to do some work and generate some result that might need to be passed to the base class' ctor.

Peter87 wrote:
Ugly as in non-optimal performance wise?

I haven't encountered a use case where it's non-performant but I meant "messy": the messy work done to initialize base members are done in the derived class' ctor.

Peter87 wrote:
If it really is a problem you could always work around it by providing a protected constructor that does no or minimal work that you can use from the derived class and possibly also an init function that you can call.

Ah yes. That would work.
Topic archived. No new replies allowed.