2

I have a Foo class that includes an array and a static constexpr variable. As a general convention, I want to write public and private variables respectively. But, compiler error occurs 's_array_size' was not declared in this scope when I compile the header code below.

#ifndef FOO_H
#define FOO_H
#include <array>
#include <cstddef>

class Foo
{
public:
    Foo();
    std::array<int, s_array_size> m_array;
private:
    constexpr static size_t s_array_size;
}

#endif

I can make s_array_size public or I can move the private section above the public section to solve the problem. However, I don't like these solutions as I want two sections public and private respectively. Is there any way to declare a constexpr static variable inside a class?

7
  • 2
    please post a minimal reproducible example and include the compiler error message in the question Commented May 8 at 7:33
  • 7
    What's the point of it being private when m_array isn't? Commented May 8 at 7:34
  • "However, I don't like these solutions as I want two sections public and private respectively" if you move private above public, you still have one private and one public section Commented May 8 at 8:07
  • 1
    @molbdnilo just guessing, but maybe they want to hide the specific way they set the size for m_array. E.g. tomorrow it can be a constexpr function instead of a static variable. Anyway that would be a reason to do so. Commented May 8 at 8:08
  • 1
    imho ordering members according to aesthetics is not a good idea. You have to order them according to dependencies between them. Also for initialization order matters, as members are initialized in order they are declared. Commented May 8 at 8:09

3 Answers 3

11

Move your constant upward, it must be known before you declare your array

https://godbolt.org/z/MzPh3Ps3W

#include <array>
#include <cstddef>

class Foo
{
private:
    static constexpr std::size_t s_array_size{4ul};    
public:
    Foo();
    std::array<int, s_array_size> m_array;
};
Sign up to request clarification or add additional context in comments.

1 Comment

C++23 (and later) could use 4uz.
6

The problem is not to declare a constexpr static member. You can do that:

class Foo
{
private:
    constexpr static size_t s_array_size = 0;
};

It must have an initializer though.

To use the member as size of an array, swap the two members:

class Foo
{
private:
    constexpr static size_t s_array_size = 0;
public:
    std::array<int, s_array_size> m_array;
};

Comments

1

The major issue I found with in the question is as follows:

constexpr implies inline which means the object/function must be immediately defined not declared. In other words, constexpr demands complete definition, rather than a mere declaration. If object type is not implicitly default constructible, or is trivially default constructible, the initializer expression will be mandatory.

Thus the primary fix could be:

public:
//A full definition, not a declaration 
constexpre static std::size_t s_array_size = 10;
private:
//of-course order of declaration matters too:
std::array<int, s_array_size> m_array;

In the above snippet s_array_size had to be defined before m_array were declared. The distinction between two terms definition and declaration can be looked up in any reliable C++ reference(Left for the reader 😈).

My preferred approach is logic inversion; I most often like to extract meta data out of object/class after its definition/declaration:

private: 
std::array<int, 10> m_array;
public:
//Should compile, but probably won't:
constexpr static std::integral_costant
   < std::size_t
   , decltype(m_array)::size()>
s_array_size;

But it doesn't work, because std::array::size is not static. It's just constexpr. I'd rather see it consteval static. It's not hard to define a replacement template in personal codebase though. Of course for a trivial element type as int, I could product the result by instantiation: decltype(m_array){}.size(); but that doesn't sit right with me, because I might decide to change the element type in later revisions and that solution is not generic enough.

2 Comments

decltype(m_array){}.size() isnt affected by chaning the element type of the array, is it?
Unfortunately it is. For int it works, because default constructor of trivial types is constexpr. Some user types may have deleted default constructors at all. This is a deficiency in std library. But personal code base can solve it with a simple template.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.