if constexpr vs sfinae
if constexpr vs sfinae
随着if constexpr
在c++17
中的引入,在c++14
/c++11
中使用编译时SFINAE解决的一些问题现在可以使用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
的解决方案与基于前c++17 SFINAE的等效解决方案是否相同?
可以假设编译器在评估
if constexpr
时完全放弃未验证的条件,只为满足if constexpr
条件的分支生成代码吗?标准是否为编译器指定了这样的行为?
标准规定,从[stmt.if]:
如果
if
语句的形式为if constexpr
,则条件的值应为类型为bool
的上下文转换的常量表达式;这种形式被称为constexprif语句。如果转换后的条件的值是false
,则第一个子语句是丢弃语句,否则第二个子语句(如果存在)是丢弃语句。在封闭模板化实体的实例化过程中,如果条件在实例化后不依赖于值,则不实例化丢弃的子语句(如果有)。
这里的重点是丢弃语句不是实例化的-这就是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
。一个简单的CCD_ 22将需要调用CCD_。
除非rest...
中有内容,否则if constexpr
不会实例化递归调用,因此这是有效的。
其他一切都源于缺乏实例化。不能为丢弃的语句生成任何代码。
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++指定程序可观察的行为。
这两段代码都有可观察的行为打印。
复制引用、调用接受引用并返回void的函数都是不可观察的行为。
这两个函数具有相同的可观测行为。因此,C++标准在运行时、代码大小或内存使用方面没有任何区别。
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 如何为模板化对象创建模板向量?VS正在投掷C3203
- 为什么使用SFINAE而不是函数重载
- 如何使用模板函数的函数签名进行SFINAE
- 数据成员SFINAE的C++17测试:gcc vs clang
- 使用在用于SFINAE的void_t中具有参数的方法
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 在for循环中使用auto vs decltype(vec.size())来处理字符串的向量
- 正在VS调试器中监视映射条目
- Confusion: decltype vs std::function
- 将IBM Rhapsody模型集成到VS 2019中
- VS Code "command":"make"与终端窗口中的命令行"make"不同
- 使用VS Code和CMake Tools运行自定义命令
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- C++ SFINAE : is_constructible for const char[] vs std::strin
- if constexpr vs sfinae
- VS 2013 SFINAE 缺陷的解决方法
- Clang vs. GCC vs. MSVC中的SFINAE和可见性检查——这是正确的
- SFINAE: static_assert vs std::enable_if
- Clang vs. GCC:未评估上下文中的错误会中断 SFINAE