Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class with private constructor and static array of itself

Sorry if title is confusing, I couldn't find an easy way to write it in a simple sentence. Anyways, the issue I'm 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 is meant to return an instance from a 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 a std::array of Something, unless I add a public ctor SomeThing() in the class. It works fine with old-style arrays, I can define the array, and fill it up 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 structure for this concept?

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

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

It is going to be used in arrays in other places as well. I would like to guarantee this class to always keep certain invariants towards to external users. With this friendly array, a user can accidentally create an uninitialised array of SomeThing.

Also, the lookup tables are generated using a rather complicated process, can't be done per inline, as in std::array<SomeThing, 64> SomeThing::lookup_table_0(some value)

like image 573
Gábor Buella Avatar asked Dec 30 '14 00:12

Gábor Buella


People also ask

Can we have private and static constructor in same class?

Solution 1. You can't get an instance of a static class while you can get instances of a class having private constructor via static methods. Private constructor is used, for instance, in the Singleton design pattern (see, for istance "Implementing Singleton in C#"[^]).

Can we have private static constructor?

A private constructor does not allow a class to be subclassed. A private constructor does not allow to create an object outside the class. If all the constant methods are there in our class we can use a private constructor. If all the methods are static then we can use a private constructor.

Can we create private constructor of static class in Java?

A static class can only have a static constructor and public/private does not apply since your code can never call this constructor (the CLR does). So you may not use a access modifier (public/private/...) on a static constructor. Save this answer.

Can we instantiate a class with private constructor?

Yes, we can access the private constructor or instantiate a class with private constructor. The java reflection API and the singleton design pattern has heavily utilized concept to access to private constructor.


2 Answers

The std::array<SomeThing, 64> class clearly doesn't have access to the private default constructor when it tries to define the instance. You can give it the necessary access by adding

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

to the definition of SomeThing.

like image 148
Dietmar Kühl Avatar answered Sep 20 '22 18:09

Dietmar Kühl


The solution:

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

Note: as explained here, {{}} is required to value-initialize the std::array without warnings in gcc. = {} and {} are correct but gcc warns anyway.

The key to the solution is that some form of initializer must be present.


A terminology check first: all objects are initialized in C++. There are three forms of this, default, value and zero. There are no "non-initialized" objects ; objects with no explicit initializer are called default-initialized. In some circumstances this means the member variables of the object may be indeterminate ("garbage").

What is the problem with the no-initializer version? Firstly, the constructor for std::array<SomeThing, 64> is defined as deleted because the declaration std::array<SomeThing, 64> x; would be ill-formed (due to lack of an accessible default constructor for SomeThing, of course).

That means that any code which attempts to use the default constructor for std::array<SomeThing, 64> is in turn ill-formed. The definition:

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

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

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

An initializer for a static member is in the scope of the member’s class

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

like image 28
M.M Avatar answered Sep 19 '22 18:09

M.M