如果忽略结果,则在编译时不调用 Constexpr 函数
Constexpr functions not called at compile-time if result is ignored
我正在调查 constexpr
函数的一些相当奇怪的代码覆盖结果(我使用的代码覆盖工具无法检测编译时调用),并注意到一些 constexpr
函数被调用为 runtime 函数,如果未存储函数调用的结果.
看来,对于 constexpr
函数或方法,如果将调用结果存储在运行时变量 [emphasis!!!] 或 constexpr
变量中,则call 是编译时调用(只要参数是编译时的)。如果忽略结果,则调用是运行时调用。这与我的代码覆盖工具无关;在下面的简单示例中,该行为似乎是可重复的。
您可能会争辩说,由于 constexpr
函数不能有副作用,所以如果您不 return / 使用结果,编译器做什么并不重要。我认为为了提高效率,编译器仍然会做任何它可以做的事情 constexpr
,但这既不存在也不存在......我想知道这是否是定义的行为。
这是保证您的 constexpr
函数将作为运行时调用的可移植方式吗???没有大量的用途,但一个用途是:如果你想在代码覆盖率中“为在 constexpr
函数上调用的测试归功”,只需在结束时调用相同的函数你的单元测试,并忽略结果,以便他们得到检测。
还有其他方法可以强制函数作为运行时调用,我知道,我主要对这里发生的事情感到好奇。第一次看到的时候非常意外。除非这是可移植的,否则我可能只是通过运行时对象(即使对于静态方法似乎也能解决问题)或通过运行时函数指针来调用我的 constexpr
方法。
参见下面的示例。现场演示:https://onlinegdb.com/rJao0RNGP
// Modified from
extern bool no_symbol;
struct ContextIsConstexpr {
size_t s;
constexpr ContextIsConstexpr() : s(1) {}
constexpr void operator()() {
auto ignore = s ? 1 : no_symbol;
}
};
constexpr bool tryIgnoringResult()
{
ContextIsConstexpr()();
return true;
}
constexpr void thereIsNoResult() {
ContextIsConstexpr()();
}
int main()
{
constexpr auto result1 = tryIgnoringResult(); // OK, compile-time
auto result2 = tryIgnoringResult(); // OK, compile-time
// tryIgnoringResult(); // Unexpected, runtime!
// thereIsNoResult(); // Unexpected, runtime!
}
这可能会造成混淆,但是 constexpr 函数应该只在编译时在 constexpr 上下文中调用(分配给 constexpr 变量,用于数组大小或模板参数,...)。
在常规上下文中,函数在运行时调用。优化器可能会在编译时解析该函数(对于遵循 as-if 规则的任何其他函数)。 constexpr
确实是进行优化的好提示。
You could argue that since constexpr functions cannot have side-effects
它们可能有副作用,请参见以下有效示例:
constexpr int f(int i)
{
if (i == 0) return 0;
std::cout << i << std::endl;
return i;
}
int main()
{
[[maybe_unused]] constexpr int zero = f(0); // Compile time
f(42); // runtime
}
我正在调查 constexpr
函数的一些相当奇怪的代码覆盖结果(我使用的代码覆盖工具无法检测编译时调用),并注意到一些 constexpr
函数被调用为 runtime 函数,如果未存储函数调用的结果.
看来,对于 constexpr
函数或方法,如果将调用结果存储在运行时变量 [emphasis!!!] 或 constexpr
变量中,则call 是编译时调用(只要参数是编译时的)。如果忽略结果,则调用是运行时调用。这与我的代码覆盖工具无关;在下面的简单示例中,该行为似乎是可重复的。
您可能会争辩说,由于 constexpr
函数不能有副作用,所以如果您不 return / 使用结果,编译器做什么并不重要。我认为为了提高效率,编译器仍然会做任何它可以做的事情 constexpr
,但这既不存在也不存在......我想知道这是否是定义的行为。
这是保证您的 constexpr
函数将作为运行时调用的可移植方式吗???没有大量的用途,但一个用途是:如果你想在代码覆盖率中“为在 constexpr
函数上调用的测试归功”,只需在结束时调用相同的函数你的单元测试,并忽略结果,以便他们得到检测。
还有其他方法可以强制函数作为运行时调用,我知道,我主要对这里发生的事情感到好奇。第一次看到的时候非常意外。除非这是可移植的,否则我可能只是通过运行时对象(即使对于静态方法似乎也能解决问题)或通过运行时函数指针来调用我的 constexpr
方法。
参见下面的示例。现场演示:https://onlinegdb.com/rJao0RNGP
// Modified from
extern bool no_symbol;
struct ContextIsConstexpr {
size_t s;
constexpr ContextIsConstexpr() : s(1) {}
constexpr void operator()() {
auto ignore = s ? 1 : no_symbol;
}
};
constexpr bool tryIgnoringResult()
{
ContextIsConstexpr()();
return true;
}
constexpr void thereIsNoResult() {
ContextIsConstexpr()();
}
int main()
{
constexpr auto result1 = tryIgnoringResult(); // OK, compile-time
auto result2 = tryIgnoringResult(); // OK, compile-time
// tryIgnoringResult(); // Unexpected, runtime!
// thereIsNoResult(); // Unexpected, runtime!
}
这可能会造成混淆,但是 constexpr 函数应该只在编译时在 constexpr 上下文中调用(分配给 constexpr 变量,用于数组大小或模板参数,...)。
在常规上下文中,函数在运行时调用。优化器可能会在编译时解析该函数(对于遵循 as-if 规则的任何其他函数)。 constexpr
确实是进行优化的好提示。
You could argue that since constexpr functions cannot have side-effects
它们可能有副作用,请参见以下有效示例:
constexpr int f(int i)
{
if (i == 0) return 0;
std::cout << i << std::endl;
return i;
}
int main()
{
[[maybe_unused]] constexpr int zero = f(0); // Compile time
f(42); // runtime
}