如果 constexpr 与 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
的解决方案是否与基于 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++ 指定程序可观察的行为。
两段代码都有可观察到的行为打印。
复制引用、调用采用引用的函数和 return void,都是不可观察的行为。
这两个函数具有相同的可观察行为。所以 C++ 标准对它们之间在运行时、代码大小或内存使用方面的任何差异毫无意义。
随着 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
的解决方案是否与基于 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 theif constexpr
condition? Does the standard specifies such a behavior for the compiler?
标准规定,从[stmt.if]:
If the
if
statement is of the formif constexpr
, the value of the condition shall be a contextually converted constant expression of typebool
; this form is called a constexpr if statement. If the value of the converted condition isfalse
, 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++ 指定程序可观察的行为。
两段代码都有可观察到的行为打印。
复制引用、调用采用引用的函数和 return void,都是不可观察的行为。
这两个函数具有相同的可观察行为。所以 C++ 标准对它们之间在运行时、代码大小或内存使用方面的任何差异毫无意义。