提问者:小点点

constexpr函数中的嵌套结构,以铿锵方式编译,在gcc中失败


我有以下代码的问题,我试图写一个编译时平方根函数。代码在最近的clang 6.0上编译。但是在gcc 8.0的最新版本上失败了。问题似乎是结构的初始化。

GCC输出

 error: uninitialized variable 'sqrtNewtonRaphson' in 'constexpr' context
     } sqrtNewtonRaphson;
       ^~~~~~~~~~~~~~~~~

此代码编译的最后一个gcc版本是GCC6.3,在随后的版本中,compile_time_sqrt_ver1(double)无法编译。

//------------------------------------------------------------
constexpr double
compile_time_sqrt_ver1(double x) {
    struct {
        constexpr double operator() (double x, double current, double previous) {
            return current == previous ? current : (*this)(x, 0.5 * (current + x / current), current);
        }
    } sqrtNewtonRaphson; 

    return x >= 0 && x < std::numeric_limits<double>::infinity()
           ? sqrtNewtonRaphson(x, x, 0)
           : std::numeric_limits<double>::quiet_NaN();
}

//------------------------------------------------------------
int main() {
    constexpr double test_v1 = compile_time_sqrt_ver1(24);
    return test_v1;
}

我找到的解决方案是在结构的末尾添加{},使其在最新版本的gcc和clang中编译。为什么会这样?

//------------------------------------------------------------
constexpr double
compile_time_sqrt_ver2(double x) {
    struct {
        constexpr double operator() (double x, double current, double previous) {
            return current == previous ? current : (*this)(x, 0.5 * (current + x / current), current);
        }
    } sqrtNewtonRaphson{}; // <- change {}

    return x >= 0 && x < std::numeric_limits<double>::infinity()
           ? sqrtNewtonRaphson(x, x, 0)
           : std::numeric_limits<double>::quiet_NaN();
}

//------------------------------------------------------------
int main() {
    constexpr double test_v2 = compile_time_sqrt_ver2(24);
    return test_v2;
}

共2个答案

匿名用户

GCC是正确的;叮当声是错误的。

根据[basic.types],您的匿名结构是聚合类型(因此是文本类型),因此它应该是constexpr可构造的。(N4659§6.9/10)

但是,您没有初始化聚合。聚合不是在声明时默认构造的。这就是为什么在之后添加大括号{}可以使它工作(聚合初始化)

constexpr函数要求根据[dcl.constexpr]§10.1初始化所有变量。5/3.4.5(强调矿山)

constexpr函数的定义应满足以下要求:
[…]
-其函数体应为=delete=default,或不包含

如果您添加一个constexpr构造函数,那么它也会工作(类型仍然是文本,但不再是聚合的,因此不需要显式初始化它)。但是,这也需要您命名结构。

匿名用户

只需将其作为另一个功能移出即可:

constexpr double sqrtNewtonRaphson (double x, double current, double previous) {
            return current == previous ? current : sqrtNewtonRaphson(x, 0.5 * (current + x / current), current);
        }
constexpr double compile_time_sqrt_ver2(double x) {
    return x >= 0 && x < std::numeric_limits<double>::infinity()
           ? sqrtNewtonRaphson(x, x, 0)
           : std::numeric_limits<double>::quiet_NaN();
}

//------------------------------------------------------------
int main() {
    constexpr double test_v2 = compile_time_sqrt_ver2(24);
    return test_v2;
}

EDIT使方法静态

constexpr double compile_time_sqrt_ver2(double x) {
    struct SNR{
        static constexpr double sqrtNewtonRaphson (double x, double current, double previous) {
            return current == previous ? current : sqrtNewtonRaphson(x, 0.5 * (current + x / current), current);
        }
    }; 

    return x >= 0 && x < std::numeric_limits<double>::infinity()
           ? SNR::sqrtNewtonRaphson(x, x, 0)
           : std::numeric_limits<double>::quiet_NaN();
}