Nested structure inside constexpr function, compile in clang, not executed in gcc

I am having trouble with the following code, I am trying to write a compile time square root function. The code is compiled in the latest cling 6.0. but doesn't work in latest gcc 8.0. the problem seems to be related to struct initialization.

GCC output

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

      

The latest version of gcc that this code compiles is gcc 6.3, in future versions after that, compile_time_sqrt_ver1 (double) fails to compile.

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

      

The solution I found is to add {} to the end of the struct that will compile it in the most recent version of gcc as well as clang. Why is this?

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

      

+3


source to share


2 answers


GCC is right; Klang is wrong.

In [basic.types], your anonymous structure is an aggregate type (and therefore a literal type), so it must be constexpr constructive. (N4659 ยง6.9 / 10)

However, you are not initializing your aggregate. By default, aggregates are not created by ad. This is why adding curly braces {}

afterwards allows it to work (aggregate initialization)



Function A constexpr

requires all variables to be initialized to [dcl.constexpr] ยง10.1.5 / 3.4.5 ( highlight )

A constexpr function definition must satisfy the following requirements:
[...]
- its function body must be = delete

, = default

or a compound statement that does not contain
          - a definition of a variable of non-literal type or static or duration of storing streams or for which no initialization is performed .

If you add a constructor constexpr

, then it works as well (the type remains a literal, but is no longer aggregated, so you don't need to explicitly initialize it). However, this will also require you to name your structure.

+3


source


Just move it as another function:

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

      




The method made by EDIT is static

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

      

+1


source







All Articles