【问题标题】:Getting constexpr to work with pow in C++17 on OSX让 constexpr 在 OSX 上的 C++17 中使用 pow
【发布时间】:2019-10-28 03:54:07
【问题描述】:

我正在尝试创建一个基于 Linux 的项目,该项目用 C++17 编写,可以在 OSX (Mojave) 上运行。大多数东西都编译得很好,直到我得到这个文件:ClassName.hpp:

class ClassName {

public:

    static constexpr double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1 = 2; // represents 0.99
    static constexpr double DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 = 10; // represents 1e-10
    static constexpr double DEFAULT_TARGET_INITIAL_PBAD = (1-pow(10,-DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
    static constexpr double DEFAULT_TARGET_FINAL_PBAD = pow(10,-DEFAULT_TARGET_TFINAL_DIGITS_FROM_0);
    static constexpr double DEFAULT_ERROR_TOL_DIGITS = 0.9; // as a fraction of digits in the last place from the above.
    static constexpr double DEFAULT_SAMPLE_TIME = 1;

    // more unrelated code
};

编译时出现以下错误:

error: constexpr variable
     'DEFAULT_TARGET_INITIAL_PBAD' must be initialized by a constant expression
 ...double DEFAULT_TARGET_INITIAL_PBAD = (1-pow(10,-DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
           ^                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ClassName.hpp: note: non-constexpr function 'pow<int, double>'
     cannot be used in a constant expression
   static constexpr double DEFAULT_TARGET_INITIAL_PBAD = (1-pow(10,-DEFAULT_TARGET_TINITI...
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/math.h:968:1: note:
     declared here
pow(_A1 __lcpp_x, _A2 __lcpp_y) _NOEXCEPT

所以由于某种原因,这适用于 Ubuntu 和 CentOS。我认为这与pow 的定义方式有关吗?但我不确定如何解决它,或者这是否是问题所在。我也尝试从DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 中删除constexpr 并使其成为const,但仍然遇到同样的问题。

【问题讨论】:

标签: c++ macos compiler-errors constexpr


【解决方案1】:

首先,您不能使用不是标准 C++17 中的 constexprstd::pow 不是 constepxr 的函数来初始化 constexpr 类成员。解决方法是将它们声明为const。虽然它们不能用于需要编译时间const 的地方,但它们是不可变的。一种传统的方法是在头文件中声明它们,您可以根据需要将它们包含在源文件中。然后,您需要一个定义静态 const 成员的实现文件。

如果您的代码需要编译时间 const 或 constexpr,您唯一的选择是编写自己的 pow

这是在执行 main() 之前使用非 constexpr 的函数初始化 const 静态变量的一种方法,该部分显示了该技术:

创建一个声明类的标头 constinit.h

// include header guards
// declare the static consts
struct ClassName {
    static double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1; // represents 0.99
    static double DEFAULT_TARGET_INITIAL_PBAD; // to be initialized by pow
};

创建一个初始化静态的实现文件:

#include "constinit.h"
#include <cmath>

double ClassName::DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1{ 2 }; // represents 0.99
double ClassName::DEFAULT_TARGET_INITIAL_PBAD = (1 - std::pow(10, -DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));

使用静态:

#include <iostream>
#include "constinit.h"


int main()
{
    std::cout << ClassName::DEFAULT_TARGET_INITIAL_PBAD << std::endl;
}

如果需要constexpr 进行编译时初始化,您需要定义自己的constexpr pow 函数。这适用于 C++17:

    #pragma once // or header guards per your preference

constexpr double my_pow(double x, int exp)
{
    int sign = 1;
    if (exp < 0)
    {
        sign = -1;
        exp = -exp;
    }
    if (exp == 0)
        return x < 0 ? -1.0 : 1.0;
    double ret = x;
    while (--exp)
        ret *= x;
    return sign > 0 ? ret : 1.0/ret;
}
class ClassName {
public:
    static constexpr double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1 = 2; // represents 0.99
    static constexpr double DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 = 10; // represents 1e-10
    static constexpr double DEFAULT_TARGET_INITIAL_PBAD = (1 - my_pow(10, -DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
    static constexpr double DEFAULT_TARGET_FINAL_PBAD = my_pow(10, -DEFAULT_TARGET_TFINAL_DIGITS_FROM_0);
    static constexpr double DEFAULT_ERROR_TOL_DIGITS = 0.9; // as a fraction of digits in the last place from the above.
    static constexpr double DEFAULT_SAMPLE_TIME = 1;

    // more unrelated code
};

【讨论】:

  • 谢谢!我在constexpr 函数中看到的唯一问题是while (--exp) 无效并出现以下错误:error: statement not allowed in constexpr function while (--exp)
  • @Alex 您使用什么编译器和设置?在此处与 clang 和 gcc 一起使用:godbolt.org/z/UvjyGI 您是否有机会宣布 exp 为双倍?仅适用于整数指数,因为双精度数将需要其他具有类似问题的数学函数,即不是 constexpr。
猜你喜欢
  • 1970-01-01
  • 2020-07-06
  • 2018-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-28
  • 2023-03-19
相关资源
最近更新 更多