【问题标题】:How do I correctly use enable_if in this specific function declaration?如何在这个特定的函数声明中正确使用 enable_if?
【发布时间】:2017-04-26 06:22:49
【问题描述】:

我有一个类定义为:

template <typename V, typename E>
class AdjacencyList;

其中V和E分别是顶点和边值的类型。

我目前正在尝试在AdjacencyList 中定义以下成员函数:

std::map< std::shared_ptr< Vertex<V, E> >, E > dijkstra(
    const std::shared_ptr< Vertex<V, E> > &start_vertex) const;

对于熟悉 Dijkstra 算法的人来说,只有当E 是可加且非负类型时才能正确实现。因此,如果E 是无符号整数类型,如何正确使用enable_if 构造来启用此功能?

我目前在这里看到两个令我不舒服的并发症:

  1. 返回类型和参数都与E有关。
  2. E 本身不用作类型,而是用于其他类型模板中。

因为我对enable_if 结构比较陌生,所以我会更愿意接受有关该问题的一些指导,因为这是一个相对不平凡的案例。

【问题讨论】:

    标签: c++ sfinae enable-if


    【解决方案1】:

    这实际上不是你想要做的。

    std::enable_if 的重点是导致模板替换失败。您想要替换失败的原因是因为它不是失败,您可以选择不同的重载。但是,这里没有意义,因为您不是在尝试选择不同的重载,而只是在尝试使其失败。

    所以,你应该这样做:

    std::map< std::shared_ptr< Vertex<V, E> >, E > dijkstra(
        const std::shared_ptr< Vertex<V, E> > &start_vertex) const {
        static_assert(
            std::is_unsigned<E>::value,
            "E must be unsigned.");
    }
    

    如果您尝试使用错误类型的参数调用此函数,您会收到一个很好的编译时错误,告诉您 E 必须是可加的且非负数。如果你改用enable_if,你会得到一个错误,即没有一个重载是有效的,这是一个信息量较少的错误。

    不过,这可能是一个糟糕的设计。通常,如果遇到负值,您只会抛出异常。选择强制输入为正也是不完整的,因为如果遇到溢出,算法也会失败。

    (如果你真的想做enable_if,即使这是个坏主意,你也可以......)

    std::enable_if<std::is_unsigned<E>,
                   std::map<std::shared_ptr<Vertex<V, E>>, E>::type dijkstra...
    

    如果你真的想这样编程

    C++ 对于这种类型的编程来说是错误的语言,并且锤击 C++ 直到它以这种方式工作会导致一些非常奇怪的 C++ 代码。听起来您确实想在 Agda 中编写代码。

    【讨论】:

    • 谢谢。这有助于我更好地理解 SFINAE 和 SFIAAE。这个问题让我发现自己正试图继承 Swift 和 Rust 等语言中的特征系统。我相信 C++ 会从这样的事情中受益匪浅,尽管实现它的时间已经过去了。
    • @JaredPayne:static_assert 基本上等同于 Rust 或 Swift 约束。
    【解决方案2】:

    enable_if 如果您想编写多个具有调整类型要求的重载,则很有帮助。在您的情况下,您没有任何重载,只想强制执行一些类型限制。因此,您可以只使用static_assert 来检查它们并为用户提供有意义的诊断消息,而不是通常的模板实例化失败混乱。像这样的:

    <type_traits>
    <utility>
    
    ...
    
    static_assert(::std::is_unsigned< E >::value, "edge value type must be unsigned integral type");
    static_assert(::std::is_same< E, decltype(::std::declval< E >() + ::std::declval< E >()) >, "edge value type must be addable");
    

    【讨论】:

      【解决方案3】:

      仅当 E 是无符号整数类型时才启用此功能

      我收到您的评论/请求,并直接使用&lt;type_traits&gt;提供的工具。
      如果您想使用std::enable_if,可能以下方法对您来说几乎没问题:

      std::enable_if_t<std::is_integral<E>::value and std::is_unsigned<E>::value, std::map< std::shared_ptr< Vertex<V, E> >, E >> dijkstra( const std::shared_ptr< Vertex<V, E> > &start_vertex) const;
      

      无论如何,SFINAE(S替换F失败Is Not An Error) 当你有多个选项时才有意义,否则你正在做一些我们可以称之为 Ssubstitution Failure s A总是An E错误。在这些情况下,static_assert 更合适:

      static_assert(std::is_integral<E>::value and std::is_unsigned<E>::value, "!");
      

      把它作为你函数的第一行。带有静态断言的错误消息通常也更加用户友好。

      【讨论】:

        猜你喜欢
        • 2018-08-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多