【问题标题】:Perfect forwarding trickery完美的转发诡计
【发布时间】:2021-02-04 20:49:44
【问题描述】:

如果您的对象有多个参数并且可以采用 l 或 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. 如果没有当前技术,这里有什么问题?

【问题讨论】:

  • 你可能会喜欢 Herb Sutter 的 this 视频

标签: c++ perfect-forwarding


【解决方案1】:

在您的特定情况下执行此操作的最简单方法是:

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);
   }
}

复制+移动无法逃脱,因为您绝对不想要不必要的复制。

【讨论】:

  • 您在参数中强制复制。所以你总是以最坏的情况告终!
  • @MartinYork 不,这不正确。 r 值被移动。仅复制 l 值,这也是您的版本所做的。
  • 啊。当你想到它时,它是如此明显和简单。
  • 所以 L-Valeu => 复制和移动。和 R 值 => 移动和移动。
  • @MartinYork 完全正确。它还有助于异常安全,函数现在可以是noexcept
【解决方案2】:

“如果你的对象有多个参数”

我认为,这在一定程度上取决于上下文和参数的数量。我经常遇到常见的参数化情况,在设计和性能方面,引入具有简单直接(支撑)初始化的简单参数容器似乎是一个合适的解决方案。如果您想要在 ( [0..N] ) 上进一步构建完全灵活的参数数量,这里的命名参数习惯用法(组合)可能是一种强大的附加方法。但正如我所说,这取决于上下文和可能参数的最大数量,不会仅在大多数情况下将其应用于两个参数。然后,Ayxan Haqverdili 的答案是 Herb Sutter 推荐的答案,这是我认为迄今为止可用的最灵活、最干净和非侵入性的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-12
    • 2017-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-22
    • 2011-04-05
    相关资源
    最近更新 更多