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?

Mark Ingram
  • 71,849
  • 51
  • 176
  • 230
  • Looks like you're running into [this](http://llvm.org/bugs/show_bug.cgi?id=11851). – devnull Sep 17 '13 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. – Mike Seymour Sep 17 '13 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. – Nemanja Boric Sep 17 '13 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? – BЈовић Sep 17 '13 at 11:02
  • @MikeSeymour These are similar error messages to what Clang is spitting out - http://ideone.com/kF7F8e – Mark Ingram Sep 17 '13 at 11:03
  • 2
    This comment from here (http://stackoverflow.com/questions/9789913/how-to-tell-static-assert-that-constexpr-function-arguments-are-const) 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." – Nemanja Boric Sep 17 '13 at 11:08

2 Answers2

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;
};
Mark Ingram
  • 71,849
  • 51
  • 176
  • 230
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.

user2672165
  • 2,986
  • 19
  • 27
Nemanja Boric
  • 21,627
  • 6
  • 67
  • 91