【问题标题】:How to implement perfect forwarding on a non-generic type?如何在非泛型类型上实现完美转发?
【发布时间】:2016-01-02 18:48:30
【问题描述】:

假设我有以下代码:

class Element;
typedef shared_ptr<Element> ElementPtr;

class Element
{
public:
    void add_child(const ElementPtr& elem);

private:
    vector<ElementPtr> children;
}

inline void Element::add_child(const ElementPtr& elem)
{
    children.push_back(elem);
};

我想更新add_child 以使用完美转发。我尝试更改函数定义(和声明),所以使用以下逻辑:

void Element::add_child(ElementPtr&& elem)
{
    children.push_back(forward<ElementPtr>(elem));
}

但这对于任何参数elem 是左值的调用都会崩溃。所以我想我会尝试使用模板并提出以下建议:

template <ElementPtr elem>
void Element::add_child(ElementPtr&& elem)
{
    children.push_back(forward<ElementPtr>(elem));
}

...但这不会编译。所以我把它改成了这样:

template <class T>
void Element::add_child(T&& elem)
{
    children.push_back(forward<T>(elem));
}

...编译和工作,但看起来丑陋和不合适; add_child 只会接受 ElementPtr 类型的参数,所以它的函数声明不应该反映这一点吗?

有什么方法可以实现函数的完美转发,同时在语法上证明它只接受一种变量?我基本上想要一个能够自动区分特定参数类型的左值和右值版本的函数。

【问题讨论】:

    标签: c++ c++11 move-semantics perfect-forwarding


    【解决方案1】:

    你的选择是

    1. 使用两个重载(又名“vector::push_back 做了什么”):

      void add_child(const ElementPtr& elem) { children.push_back(elem); }
      void add_child(ElementPtr&& elem) { children.push_back(std::move(elem)); }
      
    2. 使用按值获取参数的单个重载:

      void add_child(ElementPtr elem) { children.push_back(std::move(elem)); }
      
    3. SFINAE。

      template <class T,
                class = std::enable_if_t<std::is_same<ElementPtr, std::decay_t<T>>{}>>
      void Element::add_child(T&& elem)
      {
          children.push_back(forward<T>(elem));
      }
      

    选项 2 最多需要额外移动一次,但移动 shared_ptrs 很便宜,因为您不需要触及引用计数。选项 1 是有效的,但会受到组合爆炸的影响。选项 3 也很有效,但更难阅读和维护。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-19
      • 1970-01-01
      • 2023-04-04
      • 2013-11-17
      • 1970-01-01
      • 2015-04-22
      • 2021-06-17
      • 1970-01-01
      相关资源
      最近更新 更多