6

Why the code below does not compile?

#include <stdint.h>
#include <array>

class A
{
    struct Helper
    {
        static constexpr uint64_t p2(uint8_t n)
        {
            return static_cast<uint64_t>(1) << n;
        }
    };

    using DenomArray = std::array<uint64_t, Helper::p2(5)>;
};

With GCC I get:

error: 'static constexpr uint64_t A::Helper::p2(uint8_t)' called in a constant expression before its definition is complete

My understanding was that p2 function should be defined because Helper class is fully compiled.

Tried MSVC Compiler Version 19.29.30040 for x86 and GCC 12.

EDIT1:

template and non-template classes behaves differently. For example the code below compiles:

template <class T>
class A
{
private:

    struct Helper
    {
        static constexpr T p2(uint8_t n)
        {
            return static_cast<T>(1) << n;
        }
    };

    using DenomArray = std::array<T, Helper::p2(5)>;
};

using IntA = A<uint64_t>;
4
  • You should specify which version of C++ you're using (i.e. c++11, c++14...) Commented Aug 13, 2021 at 19:44
  • TL;DR of the dupe: The definition of an inline class function is not part of the class's definition. Because of that, they are not usable in defining members of the class. Commented Aug 13, 2021 at 19:45
  • @AlexisWilke see updated post. Commented Aug 13, 2021 at 19:49
  • 1
    I've reopened the Q, here is a link to the old target for reference: stackoverflow.com/questions/56799393/…. Not sure which compiler is right here as Helper::p2(5) is actually A::Helper::p2(5), and A has yet to be defined. Commented Aug 13, 2021 at 19:50

1 Answer 1

2

The place where you define DenomArray is where A::Helper is not yet fully defined, and this makes A::Helper::p2(5) non constant expression.

The interesting thing is that making A a template fixes this problem - which is probably a language defect - see here: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1626

More formally: https://eel.is/c++draft/temp.arg.nontype#2

A template-argument for a non-type template-parameter shall be a converted constant expression ([expr.const]) of the type of the template-parameter.

then https://eel.is/c++draft/expr.const#5:

An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:

and now to the part which you code violates: https://eel.is/c++draft/expr.const#5.3

(5.3) an invocation of an undefined constexpr function;

why its undefined? - because its used in a context which is regarded as a non complete context:

https://eel.is/c++draft/class#mem.general-7

A complete-class context of a class (template) is a (7.1) function body ([dcl.fct.def.general]), (7.2) default argument ([dcl.fct.default]), (7.3) default template argument ([temp.param]), (7.4) noexcept-specifier ([except.spec]), or (7.5) default member initializer

so its not said that the body of a struct is a complete-class context. You could place your using inside a function body, and it would compile:

class A
{
    struct Helper
    {
        static constexpr uint64_t p2(uint8_t n)
        {
            return static_cast<uint64_t>(1) << n;
        }
    };
   void ff() {
     using DenomArray = std::array<uint64_t, Helper::p2(5)>;
   }
};

[edit] also, see here: https://coliru.stacked-crooked.com/a/6536fdbe1ded1d01, clang error is different from gcc as it says Helper::p2(5) is non constexpr :

error: non-type template argument is not a constant expression

which is what I explained above.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.