完美的转发欺骗



如果你的对象有多个形参,并且可以接受1或r个值引用,那么构造函数的数量会迅速增加。一个小的模板技巧可以简化情况吗?或者这会导致我没有意识到的问题,或者我想得太多了,有标准的简化方法吗?

// In the following context I using std::string as an example o
// something that can be moved or copied, where we would prefer
// a move rather than a copy.
// With just two parameters.
// I need 4 constructors to do the correct thing (copy/move) correctly.
class Original
{
std::string   val1;
std::string   val2;
public:
Original(std::string const& v1, std::string const& v2): val1(v1), val2(v2) {}
Original(std::string const& v1, std::string&&      v2): val1(v1), val2(std::move(v2)) {}            
Original(std::string&&      v1, std::string const& v2): val1(std::move(v1)), val2(v2) {}            
Original(std::string&&      v1, std::string&&      v2): val1(std::move(v1)), val2(std::move(v2)) {}
};

如果参数类型是模板,我可以使用完全转发。

// This is a check to see if a template is the specific class.
// Or can be trivially constructed from the parameters.
template<typename Actual>
using ValidString = std::enable_if_t<std::is_trivially_constructible_v<std::string, Actual>, bool>;

// With these I can simplify my requirement of 4 constructors
// and simplify to a single templaed constructor that can do perfect forwarding.
class Alternative
{
std::string   val1;
std::string   val2;
public:
template<typename V1, typename V2, ValidString<V1> = true, ValidString<V2> = true>
Original(V1 v1, V2 v2): val1(std::forward<V1>(v1)), val2(std::forward<V2>(v2)) {}
};

两个问题:

  1. 是否已经有一种技术允许我错过的参数的完美转发?
  2. 如果没有当前的技术,这里的问题是什么?

根据您的具体情况,最简单的方法是:

class Original
{
std::string val1;
std::string val2;
public:
Original(std::string v1, std::string v2) noexcept
: val1{ std::move(v1) }
, val2{ std::move(v2) }
{}
};

这对于构造函数来说通常已经足够好了。它可能会复制+移动,而不仅仅是复制,但大多数类型的移动成本都很低。当赋值是有条件的时候,你可能需要做你的版本。比如:

template<typename T, typename Y>
void foo(T&& t, Y&& y)
{
if (condition)
{
varT = std::forward<T>(t);
varY = std::forward<Y>(y);
}
}

你不能逃脱copy+move,因为你肯定不想要一个不必要的拷贝。

"如果对象有多个参数">

我认为,这取决于上下文和参数的数量。我经常遇到常见的参数化情况,在这种情况下,从设计和性能的角度来看,引入具有简单直接初始化(括号)的简单参数容器似乎是一个合适的解决方案。如果您希望在([0..]N]),这里的命名参数习惯用法(组合使用)可能是一种强大的附加方法。但就像我说的,它取决于环境和可能参数的最大数量,在大多数情况下,不会只对两个参数应用这个方法。然后,Ayxan Haqverdili的答案就是Herb Sutter推荐的答案,我认为这是目前为止最灵活、最干净、最不具侵入性的方法。

最新更新