随着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++17 SFINAE的等效解相同?
发布于 2019-01-06 20:18:59
是否可以假设编译器在计算
if constexpr时完全放弃了未经验证的条件,而只为满足if constexpr条件的分支生成代码?标准是否为编译器指定了这样的行为?
该标准指定,从[stmt.if]
如果
if语句的形式是if constexpr,则条件的值应该是一个经过上下文转换的bool类型的常量表达式;这个表单被称为if constexpr语句。如果转换条件的值为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。一个简单的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)...);
}发布于 2019-01-06 23:03:17
C++指定程序可观察的行为。
这两段代码都具有可观察的行为打印。
复制引用,调用接受引用并返回无效的函数,都是不可观察的行为。
这两种功能都有相同的明显行为。因此,C++标准有必要说明它们之间在运行时、代码大小或内存使用方面的任何差异。
https://stackoverflow.com/questions/54065446
复制相似问题