【问题标题】:What is a value known at compile time?编译时已知的值是多少?
【发布时间】:2014-11-03 18:54:15
【问题描述】:

我正在学习 C++ 编程语言,我的书在一章中向我介绍了常量的概念:

必须为 constexpr 符号常量赋予一个在编译时已知的值

编译时已知的值是多少?为什么我们需要它们?

【问题讨论】:

  • 例如:int x = 3.0f;x的值在编译时是已知的。
  • 该定义具有误导性 - constexpr(在 C++ 11 中)是一个值或函数,可以在编译时进行评估。
  • “Constant expressions” prior to C++11 相关...我的回答中有很多基础工作可能会有所帮助。定义常量表达式的内容很难以简单的方式定义。

标签: c++ constants


【解决方案1】:

constant expression 表示编译器可以在编译时(即程序运行之前,编译期间)计算的表达式。

常量表达式可用于初始化标有constexpr (referring to the C++11 concept) 的变量。这样的变量向编译器提示它可以在编译时进行评估(并且可以节省宝贵的运行时周期),例如

#include <iostream>

constexpr int factorial(int n) // Everything here is known at compile time
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

int main(void)
{
    constexpr int f = factorial(4); // 4 is also known at compile time
    std::cout << f << std::endl;
    return 0;
}

Example

如果您不提供常量表达式,编译器实际上无法在编译时完成所有这些工作:

#include <iostream>

constexpr int factorial(int n) // Everything here is known at compile time
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

int main(void)
{
    int i;
    std::cin >> i;
    const int f = factorial(i); // I really can't guess this at compile time..
                                // thus it can't be marked with constexpr
    std::cout << f << std::endl;
    return 0;
}

Example

进行编译时额外工作而不是运行时工作的好处是性能提升,因为您的编译程序可能能够使用预先计算的值,而不必每次都从头开始计算.常量表达式越昂贵,程序获得的增益就越大。

【讨论】:

  • @pieroborrelli 你需要更好地学习 C++,我没有写复杂的东西。简而言之:如果您需要一个计算某些东西的函数,并且您知道它可以在编译时而不是运行时完成,C++ 可以帮助您完成它并预先计算该函数的值。在编译期间立即获得结果比在程序运行时必须计算更有效。 没有比这更简单的解释了
  • @Marco A. 好的,谢谢,只有一个问题:当我使用一个没有使用 constexpr 表示法初始化的变量时,该变量是在运行时在内存中创建的吗?
  • @pieroborrelli 它取决于变量的存储类型(例如静态、自动等),但它不能出现在 constexpr 表达式中。
【解决方案2】:

什么是编译时已知的值?

我认为谈论常量表达式更有意义。常量表达式具有在编译时已知的值。粗略地说,它可能只是一个字面量、另一个变量的名称(其值在编译时也是已知的)或包含子表达式的复杂表达式,其值在编译时已知。

引用声明用constexpr 声明的变量的初始化器需要是一个常量表达式。特别是,表达式必须满足一些要求才能成为常量表达式;列出了here

例子是

constexpr int i = 54;
constexpr float f = 684; // Compile-time conversion from int to float

constexpr int func( int i )
{
    return i*47 % 23;
}

constexpr auto value = func(i * f); // Okay; constexpr function called
                                    // with arguments that, when substituted inside,
                                    // yield constant expressions

有时一个值在编译时实际上是已知的,但根据标准,表达式不是一个常量。这包括

int i = 43;
constexpr int j = reinterpret_cast<int>(i); // Shouldn't compile. (Does with GCC)

在某些情况下,编译器可能会进行常量折叠 - 一些值可以在编译时计算,但并非必须如此。

int i = 0;

for (int j = 1; j != 10; ++j)
    i += j;

return i;

只要行为保持不变,编译器就可以完全消除循环并用55 初始化i(或简单地返回55 并消除i)。这被称为the as-if rule

【讨论】:

  • 在这种情况下,向编译器或标准 isocpp 提交缺陷报告。
  • @Surt 我认为 GCC 的开发人员很清楚这一点。
  • 我在想它应该编译,因为它最终评估为 i 并且如果 constexpr int j = i; 是好的,那么你所做的表达式也应该是。
  • @Surt 根据 [expr.const] 表达式是常量表达式,除非它涉及 reinterpret_cast。这是为了防止在编译时出现类型双关等。
  • 嗯,我知道他们允许__builtin_constant_p 我有点惊讶他们允许原始reinterpret_cast。我在尝试时遇到错误,您可以提供什么版本的示例?
【解决方案3】:

这意味着程序不需要运行来计算常数。例如:

int num = 4;

您需要这些值,以便编译器将变量放在符号表中,程序可以在其中引用和使用它们。在常量的情况下,编译器将常量符号化为不能更改的值。因此,如果您将常量声明为在运行时确定的东西,它将不起作用,因为如果常量在编译时未定义,它保持未定义。我希望这是有道理的。

【讨论】:

    猜你喜欢
    • 2017-02-06
    • 1970-01-01
    • 1970-01-01
    • 2017-05-01
    • 1970-01-01
    • 2010-09-25
    • 2011-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多