2014-12-30 2 views
7

Извините, если заголовок в замешательстве, я не мог найти простой способ написать его в простом предложении. В любом случае, этот вопрос я столкнулся:Класс с частным конструктором и статическим массивом сам по себе

// 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]; 
}; 

getThatThing функция предназначена для возврата экземпляра из таблицы перекодировки.

// 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 

Я просто не могу использовать std::array из Something, если я не добавить SomeThing() общественного CTOR в классе. Он отлично работает со старыми массивами, я могу определить массив и заполнить его в функции SomeThing::generateLookupTables(). По-видимому, у типа std::array<SomeThing, 64> нет конструктора. Любые идеи о том, как заставить его работать, или, может быть, лучше структурировать эту концепцию?

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

friend std::array<SomeThing, 64> подход кажется хорошей идеей, но:

Он будет использоваться в массивах в других местах. Я хотел бы гарантировать, что этот класс всегда будет поддерживать определенные инварианты в отношении внешних пользователей. С помощью этого дружественного массива пользователь может случайно создать неинициализированный массив из SomeThing.

Кроме того, таблицы поиска генерируются с использованием довольно сложный процесс, не может быть сделано в инлайн, как в std::array<SomeThing, 64> SomeThing::lookup_table_0(some value)

+0

Рассмотрите возможность использования 'std :: vector', если ваш класс является подвижным. Если это не движется, я думаю, что 'std :: deque' по-прежнему работает, если вы выйдете на место, конечно. – Brian

+0

Интересно, может ли это определение класса быть плохо сформированным, стандартные контейнеры должны создаваться только с полным типом. (И эта проблема возникает только из-за класса, содержащего статический член, который исходит из самого шаблона) –

+0

Мэтт: этот стандартный контейнер еще не установлен в заголовке, а только в реализации. –

ответ

4

Решение:

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

Примечание:, как описано here, {{}} требуется значение инициализировать std::array без предупреждений в GCC. = {} и {} верны, но gcc предупреждает в любом случае.

Ключом к решению является наличие какой-либо формы инициализатора.


Сначала проверяется терминология: все объекты инициализируются в C++. Существует три формы этого, по умолчанию, значение и ноль. Нет «неинициализированных» объектов; объекты без явного инициализатора называются default-initialized. В некоторых случаях это означает, что переменные-члены объекта могут быть неопределенными («мусор»).

В чем проблема с версией без инициализатора? Во-первых, конструктор для std::array<SomeThing, 64> - это , определенный как удаленный, потому что декларация std::array<SomeThing, 64> x; была бы плохо сформирована (из-за отсутствия доступного конструктора по умолчанию для SomeThing, конечно).

Это означает, что любой код, который пытается использовать конструктор по умолчанию для std::array<SomeThing, 64>, в свою очередь, плохо сформирован. Определение:

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

пытается использовать конструктор по умолчанию, поэтому он плохо сформирован. Однако, как только вы начинаете вводить инициализаторы, конструктор по умолчанию для std::array больше не играет в игру; поскольку std::array является агентом , тогда происходит инициализация совокупности, которая обходит неявно сгенерированный конструктор (ы). (Если бы были какие-то объявленные пользователем конструкторы, то это больше не было бы агрегатом).

версия с инициализаторах работает из [dcl.init]/13 (n3936):

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

Определение списка инициализации преобразует { } в { SomeThing() } здесь.

+0

Для меня это приводит к: «error: missing initializer для члена 'std :: array :: _ M_elems' [-Werror = отсутствующие поля-инициализаторы] " –

+0

@ Jarod42 исправлено. –

+0

Это по существу то же самое, что и другое решение 'lookup_table_0 (SomeThing()}'. Существует разница между неинициализированным и инициализированным значением. В любом случае, сейчас я думаю, что останусь со старыми массивами. –

4

Как ваш конструктор является частным, std::array не может его использовать.

Вы можете добавить friend class std::array<SomeThing, 64>; в раздел SomeThing, чтобы получить доступ к конструктору.

Альтернативой является использование доступного открытый конструктор для инициализации элементов массива:

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

EDIT:

Вы даже можете сделать, если у вас есть свой ход или конструктор копирования доступный:

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

Чтобы инициализировать весь массив по умолчанию.

+0

Thx, но, как я вижу, это разоблачит частный конструктор. Посмотрите мой EDIT в вопросе. –

+0

@ BuellaGábor: Кажется, я нашел волшебное решение :) – Jarod42

+0

Ничего себе, это умно. Я думаю, на самом деле он создает экземпляр, который не инициализируется памятью мусора и делает еще 63 копии этой мусорной памяти. Какая глупость, но хорошо, технически «работает». –

5

Класс std::array<SomeThing, 64> явно не имеет доступа к конструктору по умолчанию private, когда он пытается определить экземпляр. Вы можете дать ему необходимый доступ путем добавления

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

к определению SomeThing.