Как я могу сделать шаблонный конструктор, разрешающий все из l-значения ref, r-значения ref и initializer_list? - PullRequest
5 голосов
/ 05 ноября 2011

Я пытаюсь создать класс, который имеет два вектора больших последовательностей.

std::vector<double> factory() {
    return std::vector<double>{1,2,3}; // it actually generates a large sequence of double
}

struct my_class {
    my_class(const std::vector<double>& x, const std::vector<double>& y)
     : m_x(x), m_y(y)
    { }

    std::vector<double> m_x;
    std::vector<double> m_y;
};

int main() {
    my_class c(factory(), factory());
    my_class c2(factory(), {0.5, 1, 1.5});
}

Хорошо, он работает хорошо, но не использует конструктор перемещения вектора.Поэтому я попытался изменить конструктор так, чтобы он принимал ссылки на r-значения с идеальной пересылкой.

struct my_class {
    template<typename X, typename Y>
    my_class(X&& x, Y&& y
             , typename std::enable_if<std::is_convertible<X, std::vector<double> >::value &&
                                       std::is_convertible<Y, std::vector<double> >::value>::type * = 0
            )
     : m_x(std::forward<X>(x)), m_y(std::forward<Y>(y))
    { }

    std::vector<double> m_x;
    std::vector<double> m_y;
};

И теперь у меня возникла проблема.Когда я пытаюсь создать экземпляр с помощью initializer_list, у меня появляется такая ошибка:

$ g++ -W -Wall -std=gnu++0x a.cpp
a.cpp: In function ‘int main()’:
a.cpp:34:32: error: no matching function for call to ‘my_class::my_class(std::vector<double>, <brace-enclosed initializer list>)’
a.cpp:17:18: note: candidate is: my_class::my_class(const my_class&)

Я думал, что std::initializer_list<double> может не быть конвертируемым в std::vector<double>, но на самом деле он конвертируем, и ята же ошибка, когда я пытался без аргумента enable_if.Я что-то упустил?

1 Ответ

7 голосов
/ 05 ноября 2011

Предпочтительным языком является передать по значению и затем вручную переместить в список инициализатора элемента:

struct my_class {
    my_class(std::vector<double> x, std::vector<double> y)
     : m_x(std::move(x)), m_y(std::move(y))
    { }

    std::vector<double> m_x;
    std::vector<double> m_y;
};

Это будет работать со всеми возможными аргументами и будет достаточно быстрым:

  • Если вы передадите вектор lvalue , вектор будет скопирован в x и затем перемещен в m_x.
  • Если вы передадите вектор rvalue , вектор будет перемещен в x, а затем снова перемещен в m_x.
  • Если вы передадите список инициализатора, x будет инициализирован из этого списка и затем перемещен в m_x.

Альтернативой является идеальная пересылка, но клиенту становится сложнее узнать, что он может передать:

struct my_class {
    template<typename T, typename U>
    my_class(T&& x, U&& y)
     : m_x(std::forward<T>(x)), m_y(std::forward<U>(y))
    { }

    std::vector<double> m_x;
    std::vector<double> m_y;
};

Кроме того, я получаю кучу предупреждений в g ++, поэтому я бы не рекомендовал это. Просто упомяну это для полноты.

...