【问题标题】:How do I write an ADL-enabled trailing return type, or noexcept specification?如何编写启用 ADL 的尾随返回类型或 noexcept 规范?
【发布时间】:2011-11-29 23:01:37
【问题描述】:

假设我正在编写一些容器模板或其他东西。是时候专门为它专门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);
}

这非常整洁。直到我想添加一个异常规范。只要T 的交换是noexcept,我的swap 就是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 发现的swapstd::swap。我该如何处理?

【问题讨论】:

  • 这看起来高度相关并包含各种信息代码sn-ps:gcc.gnu.org/bugzilla/show_bug.cgi?id=49107(只需在页面上搜索swap
  • FWIW,如果您希望与 decltype 的尾随返回类型具有相同的启用 ADL 的行为,也可能会出现此问题。
  • 酷,不知道declval。对于受我的欺骗问题影响的许多函数,这意味着我可以改用非尾随返回类型(尽管这与我的问题无关)。

标签: c++ c++11 argument-dependent-lookup noexcept


【解决方案1】:

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

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 并委托给那里。

【讨论】:

  • 这很简洁,因为如果namespace tricks 有一个像样的名字,它可以被重用;)
  • 我刚刚意识到可能存在歧义,例如当您在 g++ 上为 std::cos 执行此操作时。见groups.google.com/a/isocpp.org/forum/?fromgroups=#!topic/…
  • 这是一个无需定义函数模板即可工作的解决方案:template &lt;typename T, typename U&gt; using swap_type = decltype(swap(std::declval&lt;T&gt;(), std::declval&lt;V&gt;()));
  • 我们需要写noexcept(noexcept(expr))吗? noexcept(expr) 还不够吗?
  • @Nawaz:嵌套的noexcept 是必要的。这里有两个不同的noexceptsnoexcept specifier 采用(常量)布尔表达式(所以noexcept(true) 与普通的noexcept 相同)。 noexcept operator 返回 true 或 false(在编译时),具体取决于其参数是否为 noexcept。所以说函数的noexcept 说明符取决于表达式是否为noexcept,你必须写noexcept(noexcept(expr))
【解决方案2】:

返回类型有一个similar problem

// 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&lt;0&gt;(std::forward&lt;Tuple&gt;(tuple)) ) 是不正确的,因为get 不在范围内。

可能的解决方法是:

  • 在封闭范围内引入一个虚拟模板(在我的示例中为get,在您的示例中为swap);这包括将using std::swap 声明放在封闭的命名空间中,但缺点是会污染命名空间。

  • 在我的示例中使用类型特征:typename std::tuple_element&lt;0, typename std::remove_reference&lt;Tuple&gt;::type&gt;::type(实际上这是有问题的,但由于不属于此处的原因),在您的示例中可能是is_nothrow_swappable&lt;T&gt;::value。然后,如果需要,特化允许将模板扩展为其他类型。

【讨论】:

    【解决方案3】:

    与其声明但不定义函数模板,这似乎可能会引起混淆,我会编写自己的类型特征(无论如何,这可能应该在标准库中)。在标准库的引导下,我将定义如下内容:

    #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 导入(以避免将其提供给所有人),但是当然,如​​果它在标准库中就没有必要了,因为它们已经可以进行不合格的调用交换。

    【讨论】:

      【解决方案4】:

      C++17 用 std::is_nothrow_swappable 解决了这个特殊用例:http://en.cppreference.com/w/cpp/types/is_swappable

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-05-11
        • 2015-09-10
        • 1970-01-01
        • 2018-05-12
        • 1970-01-01
        • 1970-01-01
        • 2019-04-22
        • 1970-01-01
        相关资源
        最近更新 更多