如何编写启用ADL的尾随返回类型或noexcept规范?

R. *_*des 46 c++ noexcept argument-dependent-lookup c++11

想象一下,我正在写一些容器模板或其他东西.现在是时候专注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);
}
Run Code Online (Sandbox Code Playgroud)

这非常整洁.直到我想添加一个异常规范.我swapnoexcept只要调换Tnoexcept.所以,我会写一些类似的东西:

template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
    noexcept(noexcept(swap(std::declval<T>(), std::declval<T>())))
Run Code Online (Sandbox Code Playgroud)

问题是,swap在那里需要ADL发现swapstd::swap.我该如何处理?

Joh*_*itb 34

我想我会把它移到一个单独的命名空间

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);
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以将整个代码移动tricks到那里并委托给那里.

  • 这很简洁,因为`namespace braicks`可以重用,如果它有一个像样的名字;) (5认同)
  • @Nawaz:嵌套的`noexcept`是必要的.这里有两个不同的"noexcepts":[`noexcept` _specifier_](http://en.cppreference.com/w/cpp/language/noexcept_spec)采用(常量)布尔表达式(所以`noexcept(true)`就像普通的"noexcept"一样.[`noexcept` _operator_](http://en.cppreference.com/w/cpp/language/noexcept)返回true或false(在编译时),具体取决于它的参数是否为"noexcept".所以说函数的'noexcept`说明符取决于表达式是否为'noexcept`,你必须编写`noexcept(noexcept(expr))`. (2认同)

Luc*_*ton 10

返回类型存在类似问题:

// 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));
}
Run Code Online (Sandbox Code Playgroud)

使用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你的情况中的潜力.然后,专业化允许在需要时为其他类型扩展模板.


Dav*_*one 5

我可以编写自己的类型特征(无论如何都应该在标准库中),而不是声明但不定义可能导致混淆的函数模板.在标准库的引导下,我将定义如下内容:

#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
Run Code Online (Sandbox Code Playgroud)

我们必须定义我们自己的命名空间以将std :: swap导入(以避免将其提供给所有人),但当然,如果它在标准库中是不必要的,因为他们已经可以对swap进行非限定调用.