如果忽略结果,则在编译时不调用Constexpr函数 [英] Constexpr functions not called at compile-time if result is ignored

查看:68
本文介绍了如果忽略结果,则在编译时不调用Constexpr函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究 constexpr 函数的一些相当奇怪的代码覆盖结果(我使用的代码覆盖工具无法检测编译时调用),并注意到一些constexpr 函数被称为 runtime 函数,如果未存储函数调用的结果,则称为 .

似乎,对于 constexpr 函数或方法,如果将调用结果存储在(在运行时变量[emphasis !!!]或 constexpr 中)变量),则该调用是编译时调用(只要参数是编译时).如果忽略结果,则该调用为运行时调用.这与我的代码覆盖工具无关.在下面的简单示例中,该行为似乎是可重复的.

您可能会争辩说,由于 constexpr 函数不会产生副作用,因此,如果不返回/不使用结果,编译器将执行什么操作也无关紧要.我认为为了提高效率,编译器仍然会做任何可能的 constexpr ,但这既不是这里也不是那里...我想知道的是,这是否甚至是定义好的行为.

这是一种可移植的方式来确保您的 constexpr 函数将在运行时被调用吗???并没有很多用途,但有一个用途是:如果您想在 constexpr 函数上调用的测试中获得荣誉",则可以使用它.在代码覆盖率方面,只需在单元测试结束时调用相同的函数,然后忽略结果,以便对其进行检测.

还有其他方法可以强制将函数调用为运行时,我知道,我主要是对这里发生的事情感到好奇.当我第一次看到它时,这是非常出乎意料的.除非这是可移植的,否则我可能只会通过运行时对象(甚至对于静态方法也能解决问题)或运行时函数指针来调用我的 constexpr 方法.

请参见下面的示例.实时演示: https://onlinegdb.com/rJao0RNGP

 //从https://stackoverflow.com/a/40410624/12854372修改extern bool no_symbol;struct ContextIsConstexpr {size_t s;constexpr ContextIsConstexpr():s(1){}constexpr void operator()(){自动忽略= s?1:no_symbol;}};constexpr bool tryIgnoringResult(){ContextIsConstexpr()();返回true;}constexpr void thereIsNoResult(){ContextIsConstexpr()();}int main(){constexpr auto result1 = tryIgnoringResult();//好,编译时自动result2 = tryIgnoringResult();//好,编译时//tryIgnoringResult();//出乎意料的运行时!//thereIsNoResult();//出乎意料的运行时!} 

解决方案

这可能会造成混淆,但是仅在constexpr上下文(分配给constexpr变量,用于数组大小或模板参数,)时才应在编译时调用constexpr函数...).

在常规情况下,函数在运行时被调用.Optimizer可能会在编译时解析该函数​​(就像遵循as-if规则的所有其他函数一样). constexpr 确实是进行优化的一个很好的提示.

您可能会争辩说,因为constexpr函数不能具有副作用

它们可能有副作用,请参见以下有效示例:

  constexpr int f(int i){如果(i == 0)返回0;std :: cout<<我<<std :: endl;返回我}int main(){[[maybe_unused]] constexpr int 0 = f(0);//编译时间f(42);//运行} 

演示

I was investigating some rather weird code-coverage results of constexpr functions (compile-time calls can't get instrumented by the code-coverage tool I use), and noticed that some constexpr functions were getting called as runtime functions, if the results of the function call were not stored.

It seems that, for constexpr functions or methods, if you store the results of the call (either in a runtime variable [emphasis!!!] or a constexpr variable), the call is a compile-time call (as long as the parameters are compile-time). If you ignore the results, the call is a runtime call. This has nothing to do with my code-coverage tool; the behavior seems to be repeatable in the simple example below.

You could argue that since constexpr functions cannot have side-effects, it doesn't matter what the compiler does if you don't return / use a result. I'd think that for efficiency, the compiler would still make whatever it can be constexpr, but that's neither here nor there... What I'm wondering is if this is even defined behavior.

Is this a portable way to guarantee your constexpr functions will be invoked as runtime??? There aren't a ton of uses for that, but one use is: if you want to "take credit for tests that were called on constexpr functions" in code coverage, by just calling the same function at the end of your unit test, and ignoring the result, so that they get instrumented.

There are other ways to force a function to get called as runtime, I know, I'm mostly curious about what's going on here. It was very unexpected when I first saw it. Unless this is portable, I'll probably just call my constexpr methods through a runtime object (which seems to do the trick even for static methods), or through a runtime function pointer.

See example below. Live demo: https://onlinegdb.com/rJao0RNGP

// Modified from https://stackoverflow.com/a/40410624/12854372

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!
}

解决方案

It might be confusing, but constexpr functions should be called at compile time only in constexpr contexts (assignation to constexpr variable, use for array size or template parameter, ...).

In regular context, functions are called at runtime. Optimizer might resolve that function at compile time (as for any other functions following the as-if rule). constexpr is indeed a good hint for optimization to happen.

You could argue that since constexpr functions cannot have side-effects

They can have side effect, see following valid example:

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
}

Demo

这篇关于如果忽略结果,则在编译时不调用Constexpr函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆