问题描述
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)) {}
(取决于您希望避免隐式复制的重要性)。
如果您以性能为目标,则确实必须使用转发参考。