Solving a constructor problem that uses static polymorphism

Viewed 57

I have a base class that allows the option to inherit from several other classes.

template<class T>
class Option1 {
protected:
    T m_optional_1;
public:
    void setOptional1(const unsigned int); // Initialized from a size
    T getOptional1() const;
};


template<class T>
class Option2 {
protected:
    T m_optional_2;
public:
    void setOptional2(T); // NOT initialized from a size
    T getOptional2() const;
};


template<class T, class ...Mixins>
class Standard : public Mixins... {
protected:
    T m_required;
public:
    Standard(const unsigned int); // Initialized from a size

    void setRequired(const unsigned int);
    T getRequired() const;
};

From this one could call the following.

Standard<std::vector<int>, Option1<std::vector<float>>, Option2<float(*)(float)>> uniqueA 
= Standard<std::vector<int>, Option1<std::vector<float>>, Option2<float(*)(float)>>(3);

Standard<std::vector<float>, Option1<std::vector<int>>> uniqueB 
= Standard<std::vector<float>, Option1<std::vector<int>>>(5);

Standard<std::vector<int>> uniqueC = Standard<std::vector<int>>(0);

Great, I now have optional class member variables depending on the declared template but can I initialize them through the constructor?

For example if I called the below.

Standard<std::vector<int>, Option1<std::vector<float>>, Option2<float(*)(float)>> uniqueA 
= Standard<std::vector<int>, Option1<std::vector<float>>, Option2<float(*)(float)>>(5);

I would expect uniqueA to hold a std::vector<int> of size 5 in m_required, m_optional_1 to hold a std::vector<float> of size 5 and m_optional_2 to be an uninitialized function pointer of type float(*)(float).

How could I achieve this, I can't see how to write a flexible constructor for Standard.

Attempt 1:

template<class T, class ...Mixins>
Standard<T>::Standard(const unsigned int size) :  {
    setOptional1(size);
    setRequired(size); // Error no guarantee Option1 is being inherited from 
}

Attempt 2:

// Create constructor for Option1
template<class T, class ...Mixins>
Standard<T>::Standard(const unsigned int size) : Mixins...(size)  { 
    // Error no such constructor exists for Option2 and to create one would be meaningless
    setRequired(size); 
}
1 Answers

I would suggest that you do not implicitly skip constructors for optional mixins that don't match, and instead let the caller initialize each mixin explicitly:

template<class T>
class Option1 {
    // ...
public:
    // Constructor(s)
    Option1(const unsigned int) { }
    // ...
};


template<class T>
class Option2 {
    // ...
public:
    // Constructor(s)
    Option2(T) { }
    // ...
};

template<class T, class ...Mixins>
class Standard : public Mixins... {
    // ...
public:

    // Forward each argument to the corresponding mixin
    template <class RequiredArg, class... OptionArgs>
    Standard(RequiredArg &&requiredArg, OptionArgs &&... optionArgs)
    : Mixins(std::forward<OptionArgs>(optionArgs))...
    , m_required(std::forward<RequiredArg>(requiredArg)) { }

    // ...
};

// Deduction guide to simplify construction
template <class Required, class... Mixins>
Standard(Required, Mixins...) -> Standard<Required, Mixins...>;

// ...

// Usage
Standard s{
    std::vector<int>(5),
    Option1<std::vector<float>>{5},
    Option2<float(*)(float)>{func}
};

See it live on Wandbox


To answer your original question, one can use a layer of SFINAE to try and construct each Mixin from the size, or default-construct it if that isn't possible.

template<class T>
class Option1 {
public:
    Option1(const unsigned int) { }
};

template<class T>
class Option2 {
public:
    Option2() { }
};

template<class T, class ...Mixins>
class Standard : public Mixins... {
protected:
    T m_required;
public:
    Standard(const unsigned int size)
    : Mixins(make_mixin<Mixins>(size))...
    , m_required(size) { }
    
private:
    template <class Mixin>
    static auto make_mixin(const unsigned int size) -> decltype(Mixin(size)) { return Mixin(size); }
    template <class Mixin>
    static auto make_mixin(...) -> Mixin { return Mixin(); }
};

See it live on Wandbox

In your particular case, maybe the simplest solution is just to give Option2 a dummy constructor taking the size (possibly private and befriending Standard).

Related