LINUX.ORG.RU

C++, статическая инициализация структур, вызов конструкторов, интересный ворнинг, тайны синтаксиса!


0

1

Сразу много нового открыл для себя, написав для прикола такой код:

#include <iostream>

struct Sobr
{
public:
	Sobr(const char *_name, int _x)
	: name(_name)
	, x( _x )
	{
		std::cout << "C Sobr(" << ((_name)?_name:"null") << ") " << (void*) this << "\n";
	}

	~Sobr()
	{
		std::cout << "~ Sobr() " << (void*) this << "\n";
	}

	const char *name;
	int x;
};

static const struct Sobr sss[4] =
{
		{ "first", 1 }
		, {"second", 2 }
		, {"third", 3 }
		, { 0, 0 }
};

int main ( void )
{

	std::cout << "Listing...\n";

	const Sobr *s = &sss[0];
	for ( ; s->x; ++s )
		std::cout << s->name << "\n";

	return 0;
}

1) Оказывается, каждый «{„xxxxx“, 99999 }» вызвал конструктор.

2) Появился странный ворнинг. Что бы он значил?

sobriety.cpp:30:1: warning: extended initializer lists only available with -std=c++0x or -std=gnu++0x
sobriety.cpp:30:1: warning: extended initializer lists only available with -std=c++0x or -std=gnu++0x
sobriety.cpp:30:1: warning: extended initializer lists only available with -std=c++0x or -std=gnu++0x
sobriety.cpp:30:1: warning: extended initializer lists only available with -std=c++0x or -std=gnu++0x

который пропадает, если писать (всё остальное работает так же)

static const struct Sobr sss[4] =
{
		Sobr ("first", 1 )
		, Sobr("second", 2 )
		, Sobr("third", 3 )
		, Sobr( 0, 0 )
};

3) причём в этом случае можно сделать такой патч:

- static const struct Sobr sss[4] =
+ static const struct Sobr sss[] =

а почему тот вариант без явного указания числа элементов не работает?

Это у тебя не POD-структура получилась. Попробуй удалить из неё конструктор с деструктором.

Gvidon ★★★★
()

extended initializer lists only available with -std=c++0x or -std=gnu++0x

Расширенные списки инициализации (инициализация POD-структур) поддерживаются только с ключами -std=c++0x or -std=gnu++0x (стандарт языка такой).

Указание количества важно, потому что компилятору нужно знать точное количество создаваемых объектов (он знает это точно во втором случае, но не знает в первом). Можно, кстати, делать вообще так:

static const struct Sobr sss[4] =
{
		"first", 1
		, "second", 2 
		, "third", 3 
		,  0, 0 
};

schizoid ★★★
()

Все правильно происходит. А при чем здесь статик то вообще?

AIv ★★★★★
()

1) Ты создал четыре объекта. Сколько раз должен быть вызван конструктор?

2) Описанным тобой способом можно инициализировать только POD'ы или что угодно но в C++11.

KblCb ★★★★★
()

Я бы с радостью убрал конструктор, только подскажите как при этом инициализировать статический массив таких структур?

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

Я бы с радостью убрал конструктор, только подскажите как при этом инициализировать статический массив таких структур?

твой вопрос содержит ответ - статический массив должен инициализироваться, а сл-но должен иметь конструктор. Если не просто инициализироваться, а чем-то, то нужен не дефолтный конструктор, а конструктор чего-то, во что можно перобразовать то, чем ты инициализируешь.

drBatty ★★
()
Ответ на: комментарий от kiverattes

А чем совет товарища выше не катит?

катит. тут вызов конструктора. Четыре раза подряд (именно потому ты и должен писать цифру 4, что-бы компилятор знал, что ему надо искать конструктор, для 8/4 == 2 аргументов, причём первый из них можно было преобразовать из строчной константы, а второй - из целой константы).

drBatty ★★
()
Ответ на: комментарий от drBatty

А с моим конструктором такое уже не работает, конструктор я похерил совсем. Почему тут вообще конструкторы работают, это ведь POD-структура, я просто хочу фиксированное их количество статических штук. Не вижу в задаче в целом необходимости в работе какого-то кода в рантайме, типа конструкторов.

kiverattes ★☆
() автор топика
Последнее исправление: kiverattes (всего исправлений: 1)
Ответ на: комментарий от kiverattes

А с моим конструктором такое уже не работает, конструктор я похерил совсем. Почему тут вообще конструкторы работают, это ведь POD-структура, я просто хочу фиксированное их количество статических штук. Не вижу в задаче в целом необходимости в работе какого-то кода в рантайме, типа конструкторов.

вот так вроде не должно быть рантайма:

#include <iostream>


struct Sobr
{
public:
	Sobr(const char *_name, int _x)
	: name(_name)
	, x( _x )
	{
		//std::cout << "C Sobr(" << ((_name)?_name:"null") << ") " << (void*) this << "\n";
	}


	~Sobr()
	{
		std::cout << "~ Sobr() " << (void*) this << "\n";
	}

	const char *name;
	int x;
};

static const struct Sobr sss[4] =
{
	{"first", 1} ,{"second", 2} , {"third", 3} ,  {0, 0}
};

int main ( void )
{

	std::cout << "Listing...\n";

	const Sobr *s = &sss[0];
	for ( ; s->x; ++s )
		std::cout << s->name << "\n";

	return 0;
}

drBatty ★★
()
Ответ на: комментарий от kiverattes

А конструктор там нужно похерить - тогда скомпилится.

тогда не инициализируется. (точнее инициализируется нулями и NULL).

drBatty ★★
()
Ответ на: комментарий от kiverattes

Ты там строчку в конструкторе закомментил, не увидишь будет рантайм или нет

если раскомментить - будет. А так ЕМНИП и не будет.

drBatty ★★
()
Ответ на: комментарий от kiverattes

Фигасе, странно.

а что именно странно? компилятор читает текст, переводит его в внутренний код (типа LLVM), а потом выкидывает всё не нужное, что только сможет. Больше жизни ненавидит рантайм, и именно потому IRL x-- - --x обычно даёт ноль. Никаких исключений. (ноль тут ещё и потому, что компилятор обожает нули, особенно на них умножать. Но и складывать тоже). Т.е. выражение x-- - --x можно вычислить тремя допустимыми способами(в одном потоке), и компилятор выбирает именно тот путь, который даёт в итоге константный нуль.

В твоём коде без отладочной печати компилятор не может выкинуть рантайм, а с ней - может и выкидывает(правда гарантии этого нет).

drBatty ★★
()
Ответ на: комментарий от kiverattes

В конструкторе же код есть - инициализация членов класса, почему не будет-то? Код-то есть.

инициализация константы константой - не код. Вот например здесь кода нет:

const int x = 17;
const int y = 10 + x;

если потом напечатать y, то код получится такой же, как печать 27. Потому-что константа y для компилятора эквивалентна константе 27.

drBatty ★★
()
Ответ на: комментарий от drBatty

ЗЫЖ и да, инициализация глобальной переменной константой - тоже не код. Именно для этого и придумали

class foo {
 int x;
 foo(int x1) : x(x1) {}
};
потому-что вот это - уже вроде как код:
class foo {
 int x;
 foo(int x1)
 {
   x(x1);
 }
};
(хотя оптимизирующий компилятор может и выкинуть этот код тоже).

drBatty ★★
()
Ответ на: комментарий от drBatty

Странно то, что у меня совет того товарища компилируется и в памяти получается ожидаемое.

kiverattes ★☆
() автор топика
Ответ на: комментарий от drBatty
#include <iostream>

struct Sobr
{
    const char *name;
    int x;
};

static const struct Sobr sss[4] =
{
	"first", 1
	, "second", 2
	, "third", 3
	, 0, 0
};

int main ( void )
{
    std::cout << "Listing...\n";

    const Sobr *s = &sss[0];
    for ( ; s->x; ++s )
        std::cout << s->name << "\n";

    return 0;
}
xx@(none) ~ $ g++ sobriety.cpp ; ./a.out 
Listing...
first
second
third
kiverattes ★☆
() автор топика
Ответ на: комментарий от drBatty

Хорошо, это вроде бы понятно.

Правильно я понимаю, что если я пишу «static» у какой-то переменной или массива, то память под это дело будет выделена статическая, то есть в виде секции исполняемого файла? То есть, на момент загрузки, например, ELF-файла загрузчиком, эта память уже будет «выделена» (не через malloc, а загрузчиком) тупо копированием байтов из ELF-файла в ОЗУ?

Вопрос только в том, как в эту секцию ELF-файла поместить 4 мои структуры уже готовые, чтобы они туда динамически не записывались путём выполнения конструкторов в рантайме сразу после загрузки ELF-файла.

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

ну если конструктора нет, то это _тривиальный_ тип, и его в C++11 можно как POD заюзать. А если конструктор есть, то даже если кода нет, и даже если структура легко в память ложится, но компилятору её уже как POD не заюзать. Скажем так - если конструктор есть, то код - всегда есть. Хотя возможно его можно выкинуть из рантайма во «время компиляции». При этом выкидывании расположение полей в памяти может быть разным, как и способ их инициализации, а значит тупо по байтам наложить это уже будет невозможно.

drBatty ★★
()
Ответ на: комментарий от drBatty

ну если конструктора нет, то это _тривиальный_ тип, и его в C++11 можно как POD заюзать. А

ЕМНИП, в С++11 POD - это класс без таблицы виртуальных методов, т.е. конструктор в POD может быть

wota ★★
()
Ответ на: комментарий от kiverattes

Правильно я понимаю, что если я пишу «static» у какой-то переменной или массива, то память под это дело будет выделена статическая, то есть в виде секции исполняемого файла?

если это внутри функции - да. Если внутри описания класса - то это значит, что статическая память выделится, и будет доступна во всех экземплярах такого класса, и даже сама по себе.

Если глобально - то это совсем другое. В этом случае переменная всегда static, это уже видимость из других модулей регулирует.

То есть, на момент загрузки, например, ELF-файла загрузчиком, эта память уже будет «выделена» (не через malloc, а загрузчиком) тупо копированием байтов из ELF-файла в ОЗУ?

именно так. Или нулём(NULL), если ничего не сказано про инициализацию в коде. А вообще - сложная очень тема. Там много нюансов.

Вопрос только в том, как в эту секцию ELF-файла поместить 4 мои структуры уже готовые, чтобы они туда динамически не записывались путём выполнения конструкторов в рантайме сразу после загрузки ELF-файла.

с нетривиальным конструктором - никак. Если я всё правильно понял. С тривиальным - как я выше показал. C++, статическая инициализация структур, вызов конструкторов, интересный ворнинг, тайны синтаксиса! (комментарий)

Но гарантию ищи в стандарте, если найдёшь ☺

drBatty ★★
()
Ответ на: комментарий от drBatty

может, но их статически в C++11 НЕ инициализировать, как я понял

по ссылке как раз явно написано, что можно

wota ★★
()
Ответ на: комментарий от drBatty

с нетривиальным конструктором - никак. Если я всё правильно понял. С тривиальным - как я выше показал.

А нетривиальный и не нужен. Нужна простая структура из набора встроенных типов const char*, int и т.п. Это ведь банальный POD? То есть для того, чтобы в статическую секцию ELF-файла записались 4 мои готовые УЖЕ инициализированные структуры, всё равно нужно оформлять конструктор (тривиальный)?

kiverattes ★☆
() автор топика
Ответ на: комментарий от wota

по ссылке как раз явно написано, что можно

A type that is trivial can be statically initialized

однако, ваше C++, статическая инициализация структур, вызов конструкторов, интересный ворнинг, тайны синтаксиса! (комментарий) определение - это определение НЕ тривиального класса. Обратите внимание - там два POD, и только один может статически инициализироваться.

drBatty ★★
()
Ответ на: комментарий от kiverattes

А нетривиальный и не нужен. Нужна простая структура из набора встроенных типов const char*, int и т.п. Это ведь банальный POD? То есть для того, чтобы в статическую секцию ELF-файла записались 4 мои готовые УЖЕ инициализированные структуры, всё равно нужно оформлять конструктор (тривиальный)?

да. надо же где-то записать инициализацию полей класса. Инициализаторы не входят в тело конструктора, и насколько я знаю, не делает его нетривиальным. Нетривиальным конструктор станет если только какой-то код в тело записать.

drBatty ★★
()
Ответ на: комментарий от drBatty

А зачем обязательно писать эти инициализаторы полей класса/структуры, если эта структура POD и компилятор мог бы «спроецировать» {«first», 1} на типы const char*, int?

kiverattes ★☆
() автор топика
Ответ на: комментарий от drBatty

однако, ваше C++, статическая инициализация структур, вызов конструкторов, интересный ворнинг, тайны синтаксиса! (комментарий) определение - это определение НЕ тривиального класса. Обратите внимание - там два POD, и только один может статически инициализироваться.

да, но оба могут иметь конструкторы

wota ★★
()
Последнее исправление: wota (всего исправлений: 1)
Ответ на: комментарий от drBatty

хотя прочитал выше - да, я не о том немного написал

wota ★★
()
Ответ на: комментарий от kiverattes

А зачем обязательно писать эти инициализаторы полей класса/структуры, если эта структура POD и компилятор мог бы «спроецировать» {«first», 1} на типы const char*, int?

для статической инициализации важен порядок. Можно поменять,

struct Sobr
{
public:
·   Sobr(const char *_name, int _x)
·   : x(_x), name(_name)
·   {
·   ·   //std::cout << "C Sobr(" << ((_name)?_name:"null") << ") " << (void*) this << "\n";
·   }

правда будет

ka.cpp: In constructor «Sobr::Sobr(const char*, int)»:
ka.cpp:20:6: предупреждение: «Sobr::x» будет инициализирован после [-Wreorder]
(это с -Wall конечно).

PS: не ставь пожалуйста так запятую - читать/править неудобно. Удобнее, если запятая как в русском языке.

drBatty ★★
()
Ответ на: комментарий от drBatty

1) А какая разница в этом самом порядке?

2) Про запятую прочитал у продвинутых чуваков в блогах - мол таким образом сразу видно, что после каждой строчки она есть - проще заметить пропуск, если строчки разной длины.

kiverattes ★☆
() автор топика
Ответ на: комментарий от kiverattes

1) А какая разница в этом самом порядке?

бывает иногда. В сложных случаях.

2) Про запятую прочитал у продвинутых чуваков в блогах - мол таким образом сразу видно, что после каждой строчки она есть - проще заметить пропуск, если строчки разной длины.

пропуск чего?

Я конечно за продвинутых посанов не отвечу, но ИМХО большинство авторов гайдлайнов тебя не поймут.

drBatty ★★
()

смешались вместе кони, люди

1) гнусный компилятор цепляет к твоей программе пару файлов crt*.o, которые (несмотря на свой скромный размер) содержат сотни уличной магии. именно поэтому не стоит объявлять глобальные переменные; хотя бы пока ты не поймёшь что происходит до вызова main().

2) потому что в С++98 это не POD-тип. с этим уже, вроде, разобрались.

3) потому что в вопросе 2 компилятор не уверен, что понял чего именно ты хочешь.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.