Как написать конечный тип возврата с поддержкой ADL или спецификацию noexcept? - PullRequest
46 голосов
/ 03 октября 2011

Представьте, что я пишу какой-то контейнерный шаблон или что-то в этом роде. И приходит время специализироваться на этом std::swap. Как хороший гражданин, я включу ADL, выполнив что-то вроде этого:

template <typename T>
void swap(my_template<T>& x, my_template<T>& y) {
    using std::swap;
    swap(x.something_that_is_a_T, y.something_that_is_a_T);
}

Это очень аккуратно и все. Пока я не хочу добавить спецификацию исключений. Мой swap равен noexcept, пока своп для T равен noexcept. Итак, я бы написал что-то вроде:

template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
    noexcept(noexcept(swap(std::declval<T>(), std::declval<T>())))

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

Ответы [ 4 ]

34 голосов
/ 03 октября 2011

Я думаю, я бы переместил его в отдельное пространство имен

namespace tricks {
    using std::swap;

    template <typename T, typename U>
    void swap(T &t, U &u) noexcept(noexcept(swap(t, u)));
}

template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
  noexcept(noexcept(tricks::swap(std::declval<T>(), std::declval<T>()))) 
{
    using std::swap;
    swap(x.something_that_is_a_T, y.something_that_is_a_T);
}

В качестве альтернативы вы можете переместить весь код в tricks и делегировать туда.

10 голосов
/ 03 октября 2011

Существует аналогичная проблема для типов возврата:

// Want to be compatible with both boost::tuple and std::tuple
template<typename Tuple>
auto first(Tuple&& tuple)
-> /* ??? */
{
    // Introduce name into scope
    using std::get;
    // but ADL can still pick boost::get for boost::tuple
    return get<0>(std::forward<Tuple>(tuple));
}

Использование decltype( get<0>(std::forward<Tuple>(tuple)) ) неверно, поскольку get не входит в область действия.

Возможные обходные пути:

  • Представление фиктивного шаблона (get в моем примере, swap в вашем случае) в прилагаемой области видимости;это включает в себя помещение объявления using std::swap во вложенное пространство имен с недостатком загрязнения пространства имен.

  • Использование черты типа: typename std::tuple_element<0, typename std::remove_reference<Tuple>::type>::type (на самом деле это проблематично, но дляпричины, которые здесь не относятся) в моем примере, и потенциал is_nothrow_swappable<T>::value в вашем случае.Специализации затем позволяют расширять шаблон для других типов, если это необходимо.

5 голосов
/ 07 декабря 2013

Вместо того, чтобы объявлять, но не определять шаблон функции, который, вероятно, может вызвать путаницу, я бы написал свою собственную черту типа (которая в любом случае должна быть в стандартной библиотеке). Следуя примеру стандартной библиотеки, я бы определил что-то вроде следующего:

#include <type_traits>
#include <utility>

namespace adl {

using std::swap;

template<typename T, typename U>
struct is_nothrow_swappable : std::integral_constant<
    bool,
    noexcept(swap(std::declval<T &>(), std::declval<U &>()))
> {
};

}   // namespace adl

Мы должны определить наше собственное пространство имен для импорта std :: swap (чтобы не давать его всем), но, конечно, если бы оно было в стандартной библиотеке, в этом не было бы необходимости, потому что они уже могут делать неквалифицированные вызовы поменяться.

1 голос
/ 06 февраля 2017

C ++ 17 решил этот конкретный вариант использования с помощью std :: is_nothrow_swappable: http://en.cppreference.com/w/cpp/types/is_swappable

...