提问人:francesco 提问时间:1/7/2019 最后编辑:Barryfrancesco 更新时间:1/7/2019 访问量:3548
如果 constexpr 与 sfinae
if constexpr vs sfinae
问:
随着 in 的引入,一些使用编译时 SFINAE in / 解决的问题现在可以使用 ,语法更简单。if constexpr
c++17
c++14
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;
}
该例程使用 中的 SFINAE 技术,而使用 来执行相同的工作。print_sfinae
c++11
print_ifconstexpr
if constexpr
是否可以假设编译器在评估时完全丢弃了未经验证的条件,只为满足条件的分支生成代码?标准是否为编译器指定了这种行为?if constexpr
if constexpr
更一般地说,就效率和生成的代码而言,基于的解决方案是否与基于 c++17 之前 SFINAE 的等效解决方案相同?if constexpr
答:
是否可以假设编译器在评估时完全丢弃了未经验证的条件,只为满足条件的分支生成代码?标准是否为编译器指定了这种行为?
if constexpr
if constexpr
该标准规定,从 [stmt.if]:
如果语句的形式是 ,则条件的值应为上下文转换的常量表达式 ;这种形式称为 constexpr if 语句。如果转换条件的值为 ,则第一个子语句是丢弃语句,否则第二个子语句(如果存在)是丢弃语句。在封闭模板化实体的实例化过程中,如果条件在实例化后不依赖于值,则不会实例化丢弃的子语句(如果有)。
if
if constexpr
bool
false
这里的重点是 discard 语句没有被实例化 - 这是作为语言功能背后的全部目的,允许你编写: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++ 指定程序可观察行为。
这两段代码都具有可观察的行为打印。
复制引用、调用接受引用并返回 void 的函数都是不可观察的行为。
这两个函数具有相同的可观察行为。因此,C++ 标准对它们之间在运行时、代码大小或内存使用方面的任何差异都有说明。
上一个:-O1 改变浮点数学
评论
print
int dummy[] = {0, ((std::cout << xs << std::endl), 0)...}; static_cast<void>(dummy); // Avoid warning for unused var