How do I write an ADL-enabled trailing return type, or noexcept specification?

后端 未结 4 1882
野趣味
野趣味 2020-12-04 16:47

Imagine I\'m writing some container template or something. And the time comes to specialize std::swap for it. As a good citizen, I\'ll enable ADL by doing somet

相关标签:
4条回答
  • 2020-12-04 17:01

    I think I would move it into a separate namespace

    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);
    }
    

    Alternatively you can move the whole code up into tricks and delegate to there.

    0 讨论(0)
  • 2020-12-04 17:02

    C++17 has solved this particular use case with std::is_nothrow_swappable: http://en.cppreference.com/w/cpp/types/is_swappable

    0 讨论(0)
  • 2020-12-04 17:04

    Rather than declaring but not defining a function template, which seems likely to cause confusion, I would write my own type trait (which is what should probably be in the standard library, anyway). Following the lead of the standard library, I would define something like the following:

    #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
    

    We have to define our own namespace to import std::swap into (to avoid giving it to everyone), but of course, if it were in the standard library that wouldn't be necessary because they can already make unqualified calls to swap.

    0 讨论(0)
  • 2020-12-04 17:22

    There is a similar problem for return types:

    // 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));
    }
    

    Using decltype( get<0>(std::forward<Tuple>(tuple)) ) isn't correct as get isn't in scope.

    Possible workarounds are:

    • Introducing a dummy template (get in my example, swap in your case) in the enclosing scope; this includes putting the using std::swap declaration in the enclosing namespace, with the drawback of polluting the namespace.

    • Use of a type trait: typename std::tuple_element<0, typename std::remove_reference<Tuple>::type>::type (actually this one is problematic but for reasons that don't belong here) in my example, and a potential is_nothrow_swappable<T>::value in your case. Specializations then allow the template to be extended for other types if need be.

    0 讨论(0)
提交回复
热议问题