Получение constexpr для работы с Pow в C ++ 17 на OSX - PullRequest
1 голос
/ 28 октября 2019

Я работаю над попыткой заставить проект на основе 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? Но я не уверен, как это исправить, или если это даже проблема. Я также попытался удалить constexpr из DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1 и DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 и сделать их const, но все равно столкнулся с той же проблемой.

1 Ответ

1 голос
/ 29 октября 2019

Во-первых, вы не можете инициализировать constexpr членов класса функциями, которые не constexpr и std::pow не constepxr в стандартном C ++ 17. Обходной путь должен объявить их const. Хотя их нельзя использовать в местах, где требуется время компиляции const, они неизменны. Традиционный подход заключается в объявлении их в заголовке, который вы при необходимости включаете в исходные файлы. Затем вам нужен один файл реализации, который определяет статические члены const.

Если ваш код требует const или constexpr времени компиляции, ваш единственный вариант - написать свой pow.

Вот один из способовИнициализируйте статическую константу с помощью функций, которые не constexpr перед выполнением main (), используя часть вашего вопроса, в которой показана техника:

Создайте заголовок 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
};
...