How to implement perfect forwarding on a non-generic type?

不打扰是莪最后的温柔 提交于 2019-12-23 01:24:13

问题


say I have the following code:

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

And I want to update add_child to use perfect forwarding. I tried changing the function definition (and declaration) so use the following logic:

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

But this crashes for any call in which the argument elem is an lvalue. So I thought I'd try it with templates and came up with the following:

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

...But this does not compile. So I changed it to this:

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

...Which compiles and works, but seems ugly and improper; add_child will only ever accept arguments of type ElementPtr, so shouldn't its function declaration reflect this?

Is there any way to implement perfect forwarding for a function while syntactically demonstrating that it only accepts one kind of variable? I basically want a function that will automatically distinguish between lvalue and rvalue versions of a specific parameter type.


回答1:


Your options are

  1. Use two overloads (aka "what vector::push_back does"):

    void add_child(const ElementPtr& elem) { children.push_back(elem); }
    void add_child(ElementPtr&& elem) { children.push_back(std::move(elem)); }
    
  2. Use a single overload that takes its argument by value:

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

Option 2 costs up to one additional move, but moving shared_ptrs is cheap since you don't need to touch the reference count. Option 1 is efficient, but subject to combinatorial explosion. Option 3 is also efficient, but is more difficult to read and maintain.



来源:https://stackoverflow.com/questions/32960020/how-to-implement-perfect-forwarding-on-a-non-generic-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!