c++ - 如果 constexpr 与 sfinae

标签 c++ c++17 sfinae

随着if constexpr的引入在 c++17 ,在 c++14 中使用编译时 SFINAE 解决的一些问题/c++11现在可以使用 if constexpr 解决, 语法更简单。

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

#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使用来自 c++11 的 SFINAE 技术,而 print_ifconstexpr使用 if constexpr 做同样的工作.

可以假设编译器在计算 if constexpr 时完全丢弃未验证条件,只为满足 if constexpr 的分支生成代码健康)状况?标准是否为编译器指定了这样的行为?

更一般地说,就效率和生成的代码而言,是基于if constexpr 的解决方案。是否与基于 pre-c++17 SFINAE 的等效解决方案相同?

最佳答案

Can one assume that the compiler, on evaluating the if constexpr completely discards the non-verified condition and generate code only for the branch which satisfy the if constexpr condition? Does the standard specifies such a behavior for the compiler?

标准规定,从[stmt.if] :

If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool; this form is called a constexpr if statement. If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity, if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.

这里的要点是丢弃语句未实例化 - 这就是 if constexpr 作为语言特性的全部目的,允许您编写:

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

你不能用简单的 if 来做到这一点,因为那仍然需要实例化子语句 - 即使条件可以在编译时确定为 false .一个简单的 if 需要调用 print_ifconstexpr() 的能力。

if constexpr 将不会实例化递归调用,除非 rest... 中有内容,所以这是有效的。

其他一切都是因为没有实例化。丢弃的语句不能有任何生成的代码。

if constexpr 形式更容易编写,更容易理解,当然编译速度也更快。绝对喜欢它。


请注意,您的第一个示例根本不需要 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)...);
}

关于c++ - 如果 constexpr 与 sfinae,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54065446/

相关文章:

c++ - 如何在 Visual Studio 中正确设置我的构建后脚本

c++ - 如何向 std::array 添加算术运算符?

c++ - g++ 和 clang++ SFINAE 和 SFINAE 失败的不同行为

c++ - 实现 C++17 兼容的 STL 容器?

c++ - SFINAE:如果不带参数调用,则会出现不明确的重载

c++ - 检查类是否具有带签名的函数

c++ - 可变参数模板之谜

c++ - 访问二维数组中的元素

c++ - Visual Studio 中的 std::transform 使用自己的迭代器失败

c++ - 以在使用临时对象调用时生成编译器错误的方式重载方法