你能在没有模板的情况下完美转发吗

问题描述

我有一个具有多个构造函数的类:

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

现在我想向存储在对象中的构造函数添加一个选项对象。但是为了使这项工作顺利进行,我想尽可能移动选项,但在需要时复制。似乎我必须将构造函数数量加倍以支持选项的移动和复制。

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 y):          options(std::move(o)) {}
        Obj(Options&& o,std::string const& str):options(std::move(o)) {}
        Obj(Options&& o,int s)         :options(std::move(o)) {}
};

如果我使用模板,我可以使用完美转发并获得正确的效果

template<typename Options>
class Obj
{
    Options options;

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

问题在于我知道 Options 类型。

解决方法

转发引用仅适用于模板,您可以模板化构造函数并对模板参数施加限制。

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,std::string const& str):options(std::forward<Options>(o)) {}
        template <typename X = Options,char v,int s)         :options(std::forward<Options>(o)) {}
};
,

您的最后一个片段:

template <typename Options>
class Obj
{
    Options options;

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

不是完美的转发,因为模板是你的类,而不是你的函数。

完美的转发应该是:

class Obj
{
    Options options;

public:

    template <typename T> Obj(T&& o,int):            options(std::forward<T>(o)) {}
    template <typename T> Obj(T&& o,std::string const&) : options(std::forward<T>(o)) {}
    template <typename T> Obj(T&& o,int)          : options(std::forward<T>(o)) {}
};

我总结了 Taking sink parameters by rvalue reference instead of by value to enforce performant usage of interfaces 中的(性能)选项

所以除非性能真的很重要,甚至额外的动作也很重要,否则我会选择

Obj(Options o,int y) : options(std::move(o)) {}

Obj(Options&& o,int y) : options(std::move(o)) {}

(取决于您希望避免隐式复制的重要性)。

如果您以性能为目标,则确实必须使用转发参考。