blackball
blackball

Reputation: 748

How to forward all unknown parameters in constructor to initialize the member object?

I am trying to do something like this:

struct Foo {
    int _val;
    Foo(int v) : _val(v){} 
};

struct Bar {
    const std::string &_name;
    Bar(const std::string &name) : _name(name) {} 
};

template<typename T>
struct Universal {
    T _t;
    Universal(...) : _t(...) {} 
};

// I want to use Universal for Foo abd Bar in the same way:
Universal<Foo> UF(9);       // 9 is for Foo
Universal<Bar> UB("hello"); // "hello" is for bar

In the code above, I would like to forward all parameters in Universal's constructor to T's constructor.

How could I do it?

Upvotes: 1

Views: 1407

Answers (2)

You need to make Universal constructor be a variadic template, and use a parameter pack and perfect forwarding.

template<typename T>
struct Universal {
    T _t;

    template <typename... Args>
    Universal(Args&&... args) : _t(std::forward<Args>(args)...) {} 
};

Unfortunately as AndyG notes in the comments, this means that if you try and copy a non-const Universal object, the forwarding version gets preferred - so you need explicit const and non-const copy constructors!

template<typename T>
struct Universal {
    T _t;

    template <typename... Args>
    Universal(Args&&... args) : _t(std::forward<Args>(args)...) {} 

    Universal(const Universal& rhs): _t(rhs._t) {}
    Universal(      Universal& rhs): _r(rhs._t) {}

    // ... but not move constructors.
};

or use the SFINAE approach shown in this answer, to make sure the default constructor is preferred.

Upvotes: 5

bolov
bolov

Reputation: 75707

Simple:

template<typename T>
struct Universal {
    T _t;
    Universal(const T& val) : _t(val) {} 
};

iff performance is of concern or if you want to support move only types you can forward the parameter:

template<typename T>
struct Universal {
    T _t;
    template <class U>
    Universal(U&& val) : _t(std::forward<U>(val)) {} 
};

Please note we need a template parameter U for the ctor in order for the forwarding to work. T&& would be just a rvalue reference.

If you want to get more fancy, you can construct in place:

template<typename T>
struct Universal {
    T _t;
    template <class... Args>
    Universal(Args&&... args) : _t(std::forward<Args>(args)...) {} 
};

As AndyG suggested in a comment, extra work would need to be done if you want cpy and mv ctors to work as expected.

The rule is: keep it simple unless there is a good reason justifying the complications. I think that the first variant is more than enough for you.

Upvotes: 0

Related Questions