10

I'm trying to implement the following:

#include <array>
#include <cstdint>

class Class2
{
};

class Class1
{
public:
    static constexpr uint8_t GetMax() { return 5; }
    static constexpr uint8_t GetMin() { return 0; }
    static constexpr uint8_t GetCount() { return GetMax() - GetMin() + 1; }

private:
    std::array<Class2, Class1::GetCount()> m_classes;
};

But I can't get it to work because of the error:

Non-type template argument is not a constant expression

I'm using Xcode 5.0. Any ideas?

6
  • Looks like you're running into this. Commented Sep 17, 2013 at 10:50
  • I get a slightly different error: "GetCount() used before its definition". Presumably, this is because Class1 is incomplete in its member declarations. But that's not an answer because (a) I'm not sure, and (b) I don't know how to fix it. Commented Sep 17, 2013 at 10:51
  • @MikeSeymour I think you're right, because declaring an array outside of the Class1 works. But I would like to see how this can be fixed. Commented Sep 17, 2013 at 11:00
  • @MikeSeymour What do you mean with "incomplete in its member declarations"? As far as I can tell, it is complete. btw shouldn't these methods be inline? Commented Sep 17, 2013 at 11:02
  • 2
    This comment from here (stackoverflow.com/questions/9789913/…) should be relevant: "Hmm.. I think we just discussed this earlier: inline function definitions are treated as though they were defined just after the class definition; so inside the class definition they're not yet available. Note that you can always say static const int number = 256; or static constexpr int number = 256; instead." Commented Sep 17, 2013 at 11:08

2 Answers 2

2

The problem that we have here is indirectly described in 3.3.7 - Class scope:

typedef int c;
enum { i = 1 };

class X {
    char v[i]; // error: i refers to ::i
               // but when reevaluated is X::i
    int f() { return sizeof(c); } // OK: X::c
    char c;
    enum { i = 2 };
};

This paragraph should describe this a little bit more (9.2.2):

A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the class-specifier.Within the class member-specification, the class is regarded as complete within function bodies, default arguments, exception-specifications, and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.

As std::array<Class2, Class1::GetCount()> is neither of the functions bodies, default arguments, exception-specification, brace-or-equal initializers, at that point, class is considered incomplete, so I think it's up to the compiler to decide whenever it will allow this, or not - but not compiling the code is OK by the standard.

Only solutions that I can think of is the one you suggested, or moving constexprs into another (possible base) class.

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

1 Comment

Thanks for the reference, that clears things up.
2

Following Nemanja Boric's answer, I converted the static methods into static members. This isn't the fix I wanted, but it works. I suppose the remaining question is why didn't it work?

#include <array>
#include <cstdint>

class Class2
{
};

class Class1
{
public:
    static constexpr uint8_t Max = 5;
    static constexpr uint8_t Min = 0;
    static constexpr uint8_t Count = Max - Min + 1;

private:
    std::array<Class2, Class1::Count> m_classes;
};

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.