代码之家  ›  专栏  ›  技术社区  ›  francesco

如果constexpr与sfinae

  •  3
  • francesco  · 技术社区  · 6 年前

    随着 if constexpr 在里面 c++17 ,使用编译时sfinae解决的一些问题 c++14 / c++11 现在可以用 如果康斯特雷普 ,语法更简单。

    例如,考虑以下编译时递归的基本示例,以生成一个子例程,该子例程打印可变数量的参数。

    #include <iostream>
    #include <type_traits>
    
    template <typename T>
    void print_sfinae(T&& x)
    {
      std::cout << x << std::endl;
    }
    
    template <typename T0, typename... T>
    std::enable_if_t<(sizeof...(T) > 0)> print_sfinae(T0&& x, T&&... rest)
    {
      std::cout << x << std::endl;
      print_sfinae(std::forward<T>(rest)...);
    }
    
    template <typename T0, typename... T>
    void print_ifconstexpr(T0&&x, T&&... rest)
    {
      if constexpr (sizeof...(T) > 0)
             {
                std::cout << x << std::endl;
                print_ifconstexpr(std::forward<T>(rest)...);
             }
      else
          std::cout << x << std::endl;
    }
    
    int main()
    {
      print_sfinae(5, 2.2, "hello");
      print_ifconstexpr(5, 2.2, "hello");
    
      return 0;
    }
    

    例行公事 print_sfinae 使用sfinae技术 C++ 11 print_ifconstexpr 通过使用 如果康斯特雷普 .

    我们可以假设编译器在计算 如果康斯特雷普 完全丢弃未验证的条件,只为满足 如果康斯特雷普 条件?标准是否为编译器指定了这样的行为?

    更一般地说,就效率和生成的代码而言,解决方案是基于 如果康斯特雷普 与基于pre-c++17 sfinae的等效解决方案相同?

    2 回复  |  直到 6 年前
        1
  •  8
  •   Barry    6 年前

    我们可以假设编译器在计算 if constexpr 完全丢弃未验证的条件,只为满足 如果康斯特雷普 条件?标准是否为编译器指定了这样的行为?

    标准规定, [stmt.if] :

    如果 if 语句的形式为 如果康斯特雷普 ,条件值应为类型的上下文转换常量表达式。 bool ;此窗体称为 如果是 语句。如果转换条件的值为 false ,第一个子项是丢弃的语句,否则第二个子项(如果存在)是丢弃的语句。在封闭模板化实体的实例化过程中,如果条件在实例化后不依赖于值,则不会实例化丢弃的子状态(如果有)。

    这里的要点是discard语句是 未实例化 -这就是背后的全部目的 如果康斯特雷普 作为语言功能,允许您编写:

    template <typename T0, typename... T>
    void print_ifconstexpr(T0&& x, T&&... rest)
    {
        std::cout << x << std::endl;
        if constexpr (sizeof...(T) > 0) {
            print_ifconstexpr(std::forward<T>(rest)...);
        }
    }
    

    你不能用简单的 如果 ,因为这仍然需要实例化子语句-即使条件可以确定为 在编译时。简单的 如果 需要有打电话的能力 print_ifconstexpr() .

    如果康斯特雷普 不会实例化递归调用,除非 rest... 所以这是可行的。

    其他一切都源于缺乏实例化。不能为丢弃的语句生成任何代码。

    这个 如果康斯特雷普 表单更容易编写,更容易理解,而且编译速度肯定更快。当然喜欢。


    请注意,您的第一个示例根本不需要sfinae。这很好用:

    template <typename T>
    void print(T&& x)
    {
        std::cout << x << std::endl;
    }
    
    template <typename T0, typename... T>
    void print(T0&& x, T&&... rest)
    {
        std::cout << x << std::endl;
        print(std::forward<T>(rest)...);
    }
    

    同样:

    void print() { }
    
    template <typename T0, typename... T>
    void print(T0&& x, T&&... rest)
    {
        std::cout << x << std::endl;
        print(std::forward<T>(rest)...);
    }
    
        2
  •  0
  •   Yakk - Adam Nevraumont    6 年前

    C++指定程序可观察的行为。

    这两段代码都有明显的打印行为。

    复制引用、调用接受引用并返回void的函数都不是可观察的行为。

    两种功能都有相同的可观察行为。因此,C++标准对运行时、代码大小或内存之间的差异进行了说明。