Как реализовать идеальную переадресацию для неуниверсального типа?

скажем, у меня есть следующий код:

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 является lvalue. Поэтому я подумал, что попробую с шаблонами, и придумал следующее:

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, поэтому не должно ли это отражать его объявление функции?

Есть ли способ реализовать идеальную пересылку для функции, синтаксически демонстрируя, что она принимает только один тип переменной? В основном мне нужна функция, которая автоматически различает версии lvalue и rvalue для определенного типа параметра.


person C. Finegan    schedule 06.10.2015    source источник


Ответы (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. СФИНАЭ.

    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 также эффективен, но его сложнее читать и поддерживать.

person T.C.    schedule 06.10.2015