Подтвердить что ты не робот

Обмен с rvalues

Предположим, что я хочу, чтобы swap работал на rvalues ​​и не хотел писать 4 версии для всех комбинаций ссылок rvalue/lvalue (версия rvalue/rvalue является беспредметной, но это не повредит). Я придумал это:

template <typename A, typename B>
struct is_same_no_ref
    : std::is_same<
        typename std::remove_reference<A>::type,
        typename std::remove_reference<B>::type
    >
{};

template <typename A, typename B,
    typename = typename std::enable_if<is_same_no_ref<A, B>::value>::type
>
inline void my_swap(A&& a, B&& b) {
    typename std::remove_reference<A>::type t = std::move(a);
    a = std::move(b);
    b = std::move(t);
}

который работает как ожидалось. Это нормально? Или я упускаю что-то важное, что заставит меня страдать позже?

4b9b3361

Ответ 1

Пока я не вижу в вашей реализации неотъемлемого понятия, я бы сделал три предложения.

(note:). Здесь я расскажу о реализации концепции обменивания rvalues ​​как swap_rvalues)


Исключить типы const из вычитания шаблона

  • Как?

Предполагая

template <typename A, typename B>
struct is_same_no_ref
    : std::is_same<
        typename std::remove_reference<A>::type,
        typename std::remove_reference<B>::type
    >
{};

измените условие включения из std::enable_if<std::is_same_no_ref<A, B>::value> на следующее.

std::enable_if<
    std::is_same_no_ref<A, B>::value &&
    !std::is_const<typename std::remove_reference<A>::type>::value &&
    !std::is_const<typename std::remove_reference<B>::type>::value
>
  • Почему?

Без исключения const-типов из вывода шаблона, передавая константные переменные в swap_rvalues, как показано ниже,

int const a = 0, b = 0;
swap_rvalues(a, b);

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


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

  • Как?

Вместо

template<typename A, typename B, typename = typename std::enable_if<...>::type>
inline void swap_rvalues(A&& a, B&& b);

объявите его следующим образом

template<typename A, typename B>
inline typename std::enable_if<...>::type swap_rvalues(A&& a, B&& b);
  • Почему?

Несмотря на то, что очень маловероятно, возможно явное определение третьего параметра шаблона swap_rvalues, эффективно перекрывая условие включения. Это может привести к компиляции кода, который не должен и nastiness может следовать. Этого полностью избежать можно с помощью объявления типа возврата для условия включения.

Рассмотрим следующий пример.

template <
        typename A, 
        typename B, 
        typename = typename std::enable_if<
            is_same_no_ref<A, B>::value &&
            !std::is_const<typename std::remove_reference<A>::type>::value &&
            !std::is_const<typename std::remove_reference<B>::type>::value
        >::type>
inline void
swap(A&& a, B&& b) {
    typename std::remove_reference<A>::type t = std::move(a);
    a = std::move(b);
    b = std::move(t);
}

struct B;
struct A{A(){} A(B const&){}};
struct B{B(){} B(A const&){}};
swap<A, B, void>(A(), B());

Он компилируется, хотя он явно не должен! A и B даже не связаны друг с другом, они просто становятся конструктивными, если ссылаются на другие.


Код повторного использования [aka KISS]

  • Как?

Поскольку значения r уже присвоены имени, просто переадресуйте вызов std::swap вместо того, чтобы обеспечить новую реализацию swap_rvalues.

  • Почему?

Почему изобретать колесо? std::swap уже предоставляет предполагаемое поведение, когда rvalues ​​присваивается имя, поэтому почему бы не повторно использовать его?


Заключение

Окончательная реализация swap_rvalues ‡ будет выглядеть следующим образом.

template <typename A, typename B>
inline typename std::enable_if<
    is_same_no_ref<A, B>::value &&
    !std::is_const<typename std::remove_reference<A>::type>::value &&
    !std::is_const<typename std::remove_reference<B>::type>::value
>::type
swap_rvalues(A&& a, B&& b) {
    std::swap(a, b);
}

Сноска

† "К изобретать колесо - это дублировать базовый метод, который ранее был создан или оптимизирован другими."

swap_rvalues на самом деле лучше будет называться swap в реальном сценарии.