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

Я хочу разработать небольшой полиморфный класс со стиранием типов, и мне интересно, какая версия шаблонного конструктора лучше и должна использоваться.

Мы можем передать по значению:

class A
{
    ...
    template< typename T >
    A( T t ) { /* create the underlying model via std::move */ }
    ...
};

или мы можем использовать универсальную ссылку:

class A
{
    ...
    template< typename T >
    A( T &&t ) { /* create the underlying model via std::forward */ }
    ...
};

(Универсальная ссылка должна быть включена, если для случая, когда T это не сам класс и класс не копируется). Есть идеи? Обе версии выглядят одинаково для меня.

1 ответ

Они не эквивалентны, и иногда одно желательно по сравнению с другим. Звездный разговор Николая Йосуттиса - это час разговора об этом. Я настоятельно рекомендую посмотреть его хотя бы один раз.

Лично, если только вы не столкнетесь с особым случаем, когда преобразования стоят дорого, и вы хотите максимально избежать временных затрат, я бы предложил просто передать по значению и std::moveспор.

Случай где T&& более эффективно:

struct foo {
    std::string bar;

    template <typename T>
    foo(T&& t)
        :bar(std::forward<T>(t)){}
};

против:

struct foo {
    std::string bar;

    foo(std::string t)
        :bar(std::move(t)){}
};

когда вы делаете:

int main() {
    foo f("some char*");
}

В первом случае (совершенная пересылка) вы просто создаете std::string с const char* аргумент. Во втором случае вы создаете один временный (t от "some char*") и один пустой std::string объект, затем вы применяете одну операцию перемещения. Это не конец света, но первая версия просто более эффективна.

Чтобы быть абсолютно ясно о производительности:

  • Первая версия использует 1 распределение

  • вторая версия использует 1 распределение и 1 ход

и под ходом я не имею в виду std::move, поскольку он не генерирует код (это просто приведение). Под движением я подразумеваю код, который должен быть выполнен, который фактически перемещается из строки, которая является частьюstd::string(std::string&&),

Еще раз - приведенный выше пример был основан на разговоре, который я связал в начале ответа. Это действительно стоит посмотреть.

Другие вопросы по тегам