Search code examples
c++constructorforwarding

Can you perfectly forward without templates


I have a class that has multiple constructors:

class Obj
{
    public:
        Obj(int x, int y)           {}
        Obj(std::string const& str) {}
        Obj(char v, int s)          {}
};

Now I want to add an Options object to the constructor that is stored in the object. But to make this work well I want to move the options when I can but copy when I have to. It seems like I have to double up the number of constructors to support both move and copy of the options.

class Options {};

class Obj
{
    Options options;

    public:
        Obj(Options const& o, int x, int y):          options(o) {}
        Obj(Options const& o, std::string const& str):options(o) {}
        Obj(Options const& o, char v, int s)         :options(o) {}

        Obj(Options&& o, int x, int y):          options(std::move(o)) {}
        Obj(Options&& o, std::string const& str):options(std::move(o)) {}
        Obj(Options&& o, char v, int s)         :options(std::move(o)) {}
};

If I had used templates I could have used perfect forwarding and got the correct affect.

template<typename Options>
class Obj
{
    Options options;

    public:
        Obj(Options&& o, int x, int y):          options(std::forward<Options>(o)) {}
        Obj(Options&& o, std::string const& str):options(std::forward<Options>(o)) {}
        Obj(Options&& o, char v, int s)         :options(std::forward<Options>(o)) {}
};

The trouble with this is that I know the type Options.


Solution

  • Forwarding references only work with templates, you could templatize the constructors and impose restriction on template parameter.

    class Obj
    {
        Options options;
        template<typename Opt>
        using ValidOption = std::enable_if_t<std::is_same_v<Options, std::decay_t<Opt>>, bool>;
    
    
        public:
            template <typename X = Options, ValidOption<X> = true>
            Obj(X&& o, int x, int y):          options(std::forward<Options>(o)) {}
            template <typename X = Options, ValidOption<X> = true>
            Obj(X&& o, std::string const& str):options(std::forward<Options>(o)) {}
            template <typename X = Options, ValidOption<X> = true>
            Obj(X&& o, char v, int s)         :options(std::forward<Options>(o)) {}
    };