如何判断表达式是在编译时还是运行时评估的?

Posted

技术标签:

【中文标题】如何判断表达式是在编译时还是运行时评估的?【英文标题】:How to tell if expression is evaluated at compile time or runtime? 【发布时间】:2019-04-30 09:37:13 【问题描述】:

我有一个相当大的 Map 对象,我想要一个单独的列表,其中的键已排序。这将在我项目的许多其他源文件中使用。

问题是我如何知道声明/定义何时是编译时作业。如果是这种情况,我应该在哪里寻找?我的意思是如何分辨?

在以下示例中,源文件中的列表是编译时作业还是发生在运行时?

另外,有没有办法在编译时进行排序操作?

// global.h    
extern QMap<int, QString> G_MAP;
extern QList<int> G_MAP_SKEYS_SORTED; 

// global.cpp
QMap<int, QString> G_MAP =  /* some hand filled (static) data */ ;
QList<int> G_MAP_SKEYS_SORTED = G_MAP.keys();

// main.cpp
int mian() 
  // Somewhere I do the sort
  std::sort(G_ListRegistersSorted.begin(), G_ListRegistersSorted.end());

【问题讨论】:

只是一个想法:你可以static_assert 它的大小并让编译器解释。 如果从some data 构造的QMap(或QList)包含动态内存分配,根据定义,它不能在编译期间完成。 @AlgirdasPreidžius 初始化列表是手动填充的,所以它不是动态的 std::sort()constexpr 仅从 C++20 开始;所以之前(你标记了 C++11)肯定不是。 本例中唯一在编译时评估的是初始化列表: /* some hand filled (static) data */ ; 【参考方案1】:

如果将结果分配给constexpr 变量、在static_assertnoexcept 语句中使用或用作模板参数,则在编译时计算表达式。这称为 constexpr 上下文。

例如:

// Function which can calculate the fibbonacci sequence at compiletime
constexpr int fib(int n) 
    if(n == 0 || n == 1) return n;
    return fib(n - 1) + fib(n - 2); 


int main() 
    // This one is calculated at compiletime
    constexpr int fib10_at_compiletime = fib(10); 

    // This one is calculated at runtime 
    // (unless the compiler was really aggressive when doing optimizations)
    int fib10_at_runtime = fib(10);    

为了在编译时调用函数或其他东西,需要将其标记为constexpr

编译时可以做什么?

C++11:

声明变量(但不要修改它们) 调用其他 constexpr 函数 调用 constexpr 构造函数(和默认构造函数) 使用数组和std::array 使用 static_asserts 之类的东西 typedefusing 声明

C++14 补充:

您现在也可以使用 lambdas 您可以在 constexpr 函数中修改变量 你可以拥有改变成员变量的 constexpr 成员函数 您可以将引用(非 const 类型)传递给 constexpr 函数

C++20 新增功能:(C++20 将于 2020 年推出)

您现在可以分配内存 现在可以调用虚函数了 您可以拥有try-catch

std::sort constexpr 吗?

为了在 constexpr 上下文中使用函数,必须将其标记为 constexpr(这对您可以在函数中执行的操作有一组限制;这些将在下面讨论)。在 C++11 中,std::sort 不是 constexpr,因为它打破了这些限制(在 C++20 之前它不会是 constexpr)。

但是,如果您被允许使用 C++14,您可以编写自己的排序函数,在编译时工作。

完整概述: https://en.cppreference.com/w/cpp/language/constexpr

【讨论】:

谢谢!希望对您有所帮助!【参考方案2】:

另外,有没有办法在编译时进行排序操作?

简短回答:不。

长答案。

不,因为 std::sort()constexpr 仅来自 C++20(您标记了 C++11),因为 void 函数 (std::sort()) 在 C++11 中不能是 constexpr ,因为QMapQList 不是constexpr 类(如果我没记错的话),因为您还没有将GMAP 和其他涉及的对象声明为constexpr 等。

但是,假设有一个MyMap 类定义constexpr,一个MyList 类声明constexpr,一个MySort() 函数定义constexpr,你可以写类似的东西(从C++14 开始因为在 C++11 中你无法编写如此复杂的constexpr 函数)

constexpr MyList foo ()

  MyMap mm  /* some values */ ;

  MyList ml  ml.keys() ;

  MySort(ml.begin(), ml.end());

  return ml;


// ...

constexpr auto ml_final  foo() ;

注意ml_final 被声明为constexpr

这对于强制(C++20 之前)编译器在可能的情况下在编译时初始化值是必要的,或者在不可能的情况下给出编译错误。

【讨论】:

以上是关于如何判断表达式是在编译时还是运行时评估的?的主要内容,如果未能解决你的问题,请参考以下文章

sizeof 是在编译时还是运行时评估?

在引用 POM 而不是定义 POM 时评估的嵌套 Maven 属性

constexpr 对不变表达式的好处[重复]

如何判断我是在 Ammonite repl 下运行还是在脚本中运行?

MyBatis 动态SQL

iOS如何判断应用程序是在前台运行还是后台运行?