constexpr 函数中的嵌套结构,在 clang 中编译,在 gcc 中失败

Nested struct inside constexpr function, compiles in clang, fails in gcc

我在使用以下代码时遇到问题,我正在尝试编写一个编译时间平方根函数。该代码在最新的 clang 6.0 上编译。 但在最新版本的 gcc 8.0 上失败。问题似乎与结构的初始化有关。

GCC 输出

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

本代码编译的最后一个gcc版本是gcc 6.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;
}

把它作为另一个功能移出:

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;
}

编辑使方法静态化

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();
}

GCC 是对的; Clang错了。

Per [basic.types] 你的匿名结构是聚合类型(因此是文字类型),所以它应该是 constexpr 可构造的。 (N4659 §6.9/10)

但是,您并未初始化聚合。聚合不是在声明时默认构造的。这就是为什么之后添加大括号 {} 使其能够工作(聚合初始化)

constexpr 函数要求根据 [dcl.constexpr] §10.1.5/3.4.5 初始化所有变量(强调我的

The definition of a constexpr function shall satisfy the following requirements:
[...]
— its function-body shall be = delete, = default, or a compound-statement that does not contain
      — a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.

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