最近,我在constexpr函数中将一些if constexpr
修改为if
,发现它们仍然可以正常工作,并且可以在编译时进行评估。这是最小的情况:
template<int N>
constexpr bool is_negative()
{
if constexpr (N >= 0) return false;
else return true;
}
int main()
{
constexpr bool v = is_negative<1>();
}
在上述情况下,N
必须是编译类型时必须知道的,因为它是非类型的模板参数,因此if constexpr
在这里工作正常。但是,它是一个constexpr函数,因此iirc,即使我将if constexpr
替换为if
,也可能可能:
template<int N>
constexpr bool is_negative()
{
if (N >= 0) return false;
else return true;
}
int main()
{
constexpr bool v = is_negative<1>();
}
在cppref中,A constexpr function must satisfy the following requirements:
中的所有要求都未提及if
。因此,对于IIUC,即使在编译时知道所有相关变量(例如上面的if
),也必须在编译时评估constexpr函数是否包含is_negative
,这是实现定义的行为。
所以,我的结论是:
if constexpr
,所以选择的是if
,这意味着不能保证在编译时对constexpr函数进行求值,这完全取决于编译器实现if constexpr
。以上全部是我的个人想法,也许是一些重要的遗漏/误解,可以随时纠正我。问题仍然没有改变:if
和if constexpr
,对于预期在编译时评估的constexpr函数,应该优先使用它们。
参考: -What is Allowed in a constexpr Function? -Difference between "if constexpr()" Vs "if()"
答案 0 :(得分:10)
在c ++ 17之前,我们没有if constexpr,因此选择if,这意味着不能保证在编译时对constexpr函数进行求值,而这一切都取决于编译器的实现
if语句不是constexpr的事实并不意味着它不能在编译时作为constexpr表达式的一部分进行求值。在您的示例中,在两种情况下,v
都在编译时求值,因为它必须是:它是一个常量表达式。这不是实现的定义。
在c ++ 17之后,如果希望constexpr函数在编译时求值,则首选constexpr。
Constexpr if语句用来解决问题。在编译时获取constexpr函数以求值并不是问题。
下面是一个示例,其中需要constexpr if
而不是简单的if
(摘自cppreference):
template <typename T>
auto get_value(T t) {
if constexpr(std::is_pointer_v<T>)
return *t; // deduces return type to int for T = int*
else
return t; // deduces return type to int for T = int
}
尝试删除constexpr
关键字,看看会发生什么(demo)。
此外,请注意,您始终可以使用其他方法来解决该问题,但是if constexpr
具有简洁的优点。例如,下面是使用标签分配的等效get_value
:
template<typename T>
auto get_value_impl(T t, std::true_type) {
return *t;
}
template<typename T>
auto get_value_impl(T t, std::false_type) {
return t;
}
template<typename T>
auto get_value(T t) {
return get_value_impl(t, std::is_pointer<T>{});
}
答案 1 :(得分:2)
if constexpr
和if之间的区别是表达式是否始终可以在编译时执行。在您的示例中,您使用的是模板参数,因此编写哪个参数并不重要。如果您具有以下代码,则可以注意到差异:
constexpr bool is_negative(int n)
{
if (n >= 0) return false;
else return true;
}
int main(int argc, char**)
{
constexpr bool v = is_negative(1);
bool b = is_negative(argc);
return static_cast<int>(v || b);
}
对于上述代码,无法编写if constexpr
。您可以使用运行时值调用该函数。
同样,这无关紧要,因为两个代码路径均有效。当使用具有恒定值的函数时,应该进行常规的编译器优化。
if constexpr
的真正利益在于只有一条路径有效时:
template <typename T>
constexpr auto get_value(T t) {
if constexpr(std::is_pointer_v<T>)
return *t; // deduces return type to int for T = int*
else
return t; // deduces return type to int for T = int
}
如果T将是一个int,则带有*t
的代码路径无效,因为您无法取消引用int。但是,由于使用的是if constexpr
而不是if
,因此假路径中的代码仅取决于模板参数,因此在语法上仅需正确。
在搜索准则时,编译器已经要求:当其中一个代码路径无效时,请使用if constexpr
。根据参数使用if
。
对于在编译时可以使用2条有效路径计算if条件的情况,请使用if constexpr
要求即使在调试版本中也要对其进行优化,如果要逐步执行,请使用if
在调试版本中。
如果走极端,表达式可能变得太复杂,以至于编译器无法在生产版本中对其进行优化,在这种情况下,if constexpr
在热路径中可能再次变得有趣。就个人而言,我还没有遇到过这种情况,但是我没有使用太多。