Class with private constructor and static array

Sorry if the title is confusing, I could not find an easy way to write it in a simple sentence. Anyway, the problem I am facing:

// header: class SomeThing { private: SomeThing() {} // <- so users of this class can't come up // with non-initialized instances, but // but the implementation can. int some_data; // <- a few bytes of memory, the default // constructor SomeThing() doesn't initialize it public: SomeThing(blablabla ctor arguments); static SomeThing getThatThing(blablabla arguments); static void generateLookupTables(); private: // declarations of lookup tables static std::array<SomeThing, 64> lookup_table_0; static SomeThing lookup_table_1[64]; }; 

The getThatThing function getThatThing intended to return an instance from the lookup table.

  // in the implementation file - definitions of lookup tables std::array<SomeThing, 64> SomeThing::lookup_table_0; // error SomeThing Something::lookup_table_1[64]; // <- works fine 

I just can't use std::array from Something unless I add the public ctor SomeThing() to the class. It works fine with old arrays, I can define an array and populate it in the SomeThing::generateLookupTables() function. Apparently, the type std::array<SomeThing, 64> does not have a constructor. Any ideas on how to make it work, or maybe a better framework for this concept?

============== EDIT =======

The friend std::array<SomeThing, 64> approach seems like a nice idea, but:

It will also be used in arrays elsewhere. I would like to guarantee that this class will always support certain invariants regarding external users. With this friendly array, the user can accidentally create an uninitialized array of Something .

In addition, lookup tables are generated using a rather complicated process; they cannot be executed for each row, as in std::array<SomeThing, 64> SomeThing::lookup_table_0(some value)

+7
c ++ initialization c ++ 11 stdarray
source share
3 answers

Decision:

 std::array<SomeThing, 64> SomeThing::lookup_table_0 {{ }}; 

Note: as described here , <21_value> needs to initialize the value of std::array without warning in gcc. = {} and {} are correct, but gcc warns you anyway.

The key to the solution is to have some form of initializer.


First, the terminology is checked: all objects are initialized in C ++. There are three forms: default value, value and zero. There are no "uninitialized" objects; objects without an explicit initializer are called initialized by default. In some cases, this means that the member variables of the object may be undefined ("garbage").

What is the problem with a version without an initializer? First, the constructor for std::array<SomeThing, 64> is defined as deleted because the declaration is std::array<SomeThing, 64> x; will be poorly formed (due to the lack of an accessible default constructor for SomeThing , of course).

This means that any code that tries to use the default constructor for std::array<SomeThing, 64> , in turn, is poorly formed. Definition:

 std::array<SomeThing, 64> SomeThing::lookup_table_0; 

tries to use the default constructor, so it is poorly formed. However, once you start introducing initializers, the default constructor for std::array no longer plays the game; since std::array is an aggregate, then aggregate initialization occurs, which bypasses the implicitly generated constructor (s). (If there were any user-declared constructors, this would no longer be an aggregate).

The initializer version works because of [dcl.init] / 13 (n3936):

The initializer for the static member is in the scope of the member class

The definition of conversion-initialization of the list { } to { SomeThing() } here.

+4
source share

The std::array<SomeThing, 64> class obviously does not have access to the default private constructor when it tries to define an instance. You can give him the necessary access by adding

 friend class std::array<SomeThing, 64>; 

to the definition of SomeThing .

+5
source share

Since your constructor is private, std::array cannot use it.

You can add friend class std::array<SomeThing, 64>; in SomeThing to provide access to the constructor.

An alternative is to use an accessible public constructor to initialize the array element:

 std::array<SomeThing, 64> SomeThing::lookup_table_0{ SomeThing(blablabla_ctor_arguments), .. }; 

EDIT:

You can even do it if you have your own movement or copy mechanism:

 std::array<SomeThing, 64> SomeThing::lookup_table_0{ SomeThing() }; 

to initialize the entire array by default.

+4
source share

All Articles