Инициализация структуры С++
Возможно ли инициализировать структуры на С++, как указано ниже.
struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
address temp_address =
{ .city = "Hamilton", .prov = "Ontario" };
В ссылках здесь и здесь упоминается, что этот стиль можно использовать только в C. Если да, то почему это невозможно в С++? Есть ли какая-либо техническая причина, почему она не реализована на С++, или это плохая практика использования этого стиля. Мне нравится использовать этот способ инициализации, потому что моя структура большая, и этот стиль дает мне четкое представление о том, какое значение присваивается члену.
Пожалуйста, поделитесь со мной, если есть другие способы, с помощью которых мы можем достичь такой же удобочитаемости.
Я отправил следующие ссылки перед публикацией этого вопроса
Ответы
Ответ 1
Если вы хотите дать понять, что такое каждое значение инициализатора, просто разделите его на несколько строк с комментариями к каждому из них:
address temp_addres = {
0, // street_no
nullptr, // street_name
"Hamilton", // city
"Ontario", // prov
nullptr, // postal_code
};
Ответ 2
После моего вопроса результат не привел к удовлетворительному результату (потому что С++ не реализует тег-основанный init для структур), я взял трюк, который я нашел здесь: Являются ли члены структуры С++ инициализированы по умолчанию 0?
Для вас это будет означать:
address temp_address = {}; // will zero all fields in C++
temp_address.city = "Hamilton";
temp_address.prov = "Ontario";
Это, безусловно, самое близкое к тому, что вы хотели первоначально (нуль всех полей, кроме тех, которые вы хотите инициализировать).
Ответ 3
Идентификаторы полей действительно являются синтаксисом инициализатора C. В С++ просто укажите значения в правильном порядке без имен полей. К сожалению, это означает, что вам нужно дать им все (на самом деле вы можете опустить конечные поля с нулевым значением, и результат будет таким же):
address temp_address = { 0, 0, "Hamilton", "Ontario", 0 };
Ответ 4
Эта функция называется назначенными инициализаторами. Это дополнение к стандарту C99. Однако эта функция была исключена из С++ 11. Согласно языку программирования С++, четвертое издание, раздел 44.3.3.2 (функции C, не принятые на С++):
Несколько дополнений к C99 (по сравнению с C89) были намеренно не приняты в С++:
[1] Массивы переменной длины (VLA); использовать вектор или какую-либо форму динамического массива
[2] Назначенные инициализаторы; использовать конструкторы
В грамматике C99 есть назначенные инициализаторы [См. ИСО/МЭК 9899: 2011, проект Комитета N1570 - 12 апреля 2011 г.]
6.7.9 Инициализация
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designation_opt initializer
initializer-list , designationopt initializer
designation:
designator-list =
designator-list:
designator
designator-list designator
designator:
[ constant-expression ]
. identifier
С другой стороны, С++ 11 не имеет назначенных инициализаторов [См. ИСО/МЭК 14882: 2011, проект Комитета N3690 - 15 мая 2013 г.]
8.5 Инициализаторы
initializer:
brace-or-equal-initializer
( expression-list )
brace-or-equal-initializer:
= initializer-clause
braced-init-list
initializer-clause:
assignment-expression
braced-init-list
initializer-list:
initializer-clause ...opt
initializer-list , initializer-clause ...opt
braced-init-list:
{ initializer-list ,opt }
{ }
Чтобы добиться такого же эффекта, используйте конструкторы или списки инициализаторов:
Ответ 5
Вы даже можете упаковать решение Gui13 в один оператор инициализации:
struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
address ta = (ta = address(), ta.city = "Hamilton", ta.prov = "Ontario", ta);
Отказ от ответственности: я не рекомендую этот стиль
Ответ 6
Вы можете просто инициализировать через ctor:
struct address {
address() : city("Hamilton"), prov("Ontario") {}
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
Ответ 7
Как отмечали другие, это назначенный инициализатор.
Эта функция является частью С++ 20
Ответ 8
Я знаю, что этот вопрос довольно старый, но я нашел другой способ инициализации, используя constexpr и currying:
struct mp_struct_t {
public:
constexpr mp_struct_t(int member1) : mp_struct_t(member1, 0, 0) {}
constexpr mp_struct_t(int member1, int member2, int member3) : member1(member1), member2(member2), member3(member3) {}
constexpr mp_struct_t another_member(int member) { return {member1, member, member2}; }
constexpr mp_struct_t yet_another_one(int member) { return {member1, member2, member}; }
int member1, member2, member3;
};
static mp_struct_t a_struct = mp_struct_t{1}
.another_member(2)
.yet_another_one(3);
Этот метод также работает для глобальных статических переменных и даже для constexpr.
Единственным недостатком является плохой ремонтопригодность: каждый раз, когда этот метод должен быть инициализирован, необходимо изменить все методы инициализации членов.
Ответ 9
Он не реализован в С++. (также, char*
строки? Надеюсь, что нет).
Обычно, если у вас так много параметров, это довольно серьезный запах кода. Но вместо этого, почему бы не просто инициализировать структуру и затем назначить каждый член?
Ответ 10
Я мог бы здесь что-то упустить, почему бы и нет:
#include <cstdio>
struct Group {
int x;
int y;
const char* s;
};
int main()
{
Group group {
.x = 1,
.y = 2,
.s = "Hello it works"
};
printf("%d, %d, %s", group.x, group.y, group.s);
}
Ответ 11
Я нашел этот способ сделать это для глобальных переменных, что не требует изменения определения исходной структуры:
struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
затем объявить переменную нового типа, унаследованную от исходного типа структуры, и использовать конструктор для инициализации полей:
struct temp_address : address { temp_address() {
city = "Hamilton";
prov = "Ontario";
} } temp_address;
Не такой элегантный, как стиль C, хотя...
Для локальной переменной также должно быть возможно, но нужно проверить, нужна ли дополнительная memset (это, 0, sizeof (* this)),...
(Обратите внимание, что "temp_address" - это переменная типа temp_address, но этот новый тип наследуется от "адреса" и может использоваться во всех местах, где ожидается "адрес", так что это нормально.)
Ответ 12
Возможно, но только если инициализация структуры - это структура POD (простые старые данные). Он не может содержать никаких методов, конструкторов или даже значений по умолчанию.
Ответ 13
В С++ инициализаторы стиля C были заменены конструкторами, которые по времени компиляции могут гарантировать, что выполняются только действительные инициализации (то есть после инициализации члены объекта согласованы).
Это хорошая практика, но иногда предварительная инициализация удобна, как в вашем примере. ООП решает это абстрактными классами или шаблонами создания дизайна.
По-моему, использование этого безопасного способа убивает простоту, а иногда компромисс между безопасностью может быть слишком дорогостоящим, поскольку простой код не нуждается в сложном дизайне, чтобы оставаться обслуживаемым.
В качестве альтернативного решения я предлагаю определить макросы, используя lambdas, чтобы упростить инициализацию, чтобы выглядеть почти как C-стиль:
struct address {
int street_no;
const char *street_name;
const char *city;
const char *prov;
const char *postal_code;
};
#define ADDRESS_OPEN [] { address _={};
#define ADDRESS_CLOSE ; return _; }()
#define ADDRESS(x) ADDRESS_OPEN x ADDRESS_CLOSE
Макрос ADDRESS расширяется до
[] { address _={}; /* definition... */ ; return _; }()
который создает и вызывает лямбду. Параметры макроса также разделяются запятыми, поэтому вам нужно поместить инициализатор в скобки и вызвать как
address temp_address = ADDRESS(( _.city = "Hamilton", _.prov = "Ontario" ));
Вы также можете написать генерализованный инициализатор макросов
#define INIT_OPEN(type) [] { type _={};
#define INIT_CLOSE ; return _; }()
#define INIT(type,x) INIT_OPEN(type) x INIT_CLOSE
но затем вызов немного менее красивый
address temp_address = INIT(address,( _.city = "Hamilton", _.prov = "Ontario" ));
однако вы можете определить макрос ADDRESS с помощью общего макроса INIT.
#define ADDRESS(x) INIT(address,x)
Ответ 14
Сегодня я столкнулся с аналогичной проблемой, где у меня есть структура, которую я хочу заполнить тестовыми данными, которые будут переданы в качестве аргументов для функции, которую я тестирую. Я хотел иметь вектор этих структур и искал однострочный метод для инициализации каждой структуры.
Я закончил работу с конструкторской функцией в структуре, которая, как мне кажется, также была предложена в нескольких ответах на ваш вопрос.
Вероятно, неверная практика состоит в том, чтобы аргументы конструктора имели те же имена, что и переменные public-члена, требующие использования указателя this
. Кто-то может предложить отредактировать, если есть лучший способ.
typedef struct testdatum_s {
public:
std::string argument1;
std::string argument2;
std::string argument3;
std::string argument4;
int count;
testdatum_s (
std::string argument1,
std::string argument2,
std::string argument3,
std::string argument4,
int count)
{
this->rotation = argument1;
this->tstamp = argument2;
this->auth = argument3;
this->answer = argument4;
this->count = count;
}
} testdatum;
Что я использовал в своей тестовой функции для вызова тестируемой функции с помощью следующих аргументов:
std::vector<testdatum> testdata;
testdata.push_back(testdatum("val11", "val12", "val13", "val14", 5));
testdata.push_back(testdatum("val21", "val22", "val23", "val24", 1));
testdata.push_back(testdatum("val31", "val32", "val33", "val34", 7));
for (std::vector<testdatum>::iterator i = testdata.begin(); i != testdata.end(); ++i) {
function_in_test(i->argument1, i->argument2, i->argument3, i->argument4m i->count);
}
Ответ 15
Вдохновлен этим действительно аккуратным ответом: (fooobar.com/questions/78607/...)
Вы можете сделать закрытие ламбы:
// Nobody wants to remember the order of these things
struct SomeBigStruct {
int min = 1;
int mean = 3 ;
int mode = 5;
int max = 10;
string name;
string nickname;
... // the list goes on
}
,
class SomeClass {
static const inline SomeBigStruct voiceAmps = []{
ModulationTarget $ {};
$.min = 0;
$.nickname = "Bobby";
$.bloodtype = "O-";
return $;
}();
}
Или, если вы хотите быть очень модным
#define DesignatedInit(T, ...)\
[]{ T ${}; __VA_ARGS__; return $; }()
class SomeClass {
static const inline SomeBigStruct voiceAmps = DesignatedInit(
ModulationTarget,
$.min = 0,
$.nickname = "Bobby",
$.bloodtype = "O-",
);
}
С этим связаны некоторые недостатки, в основном связанные с неинициализированными членами. Судя по комментариям в связанных ответах, он компилируется эффективно, хотя я не проверял его.
В целом, я просто считаю это аккуратным подходом.
Ответ 16
В GNUC++ (кажется, устарел с 2.5, давным-давно :) Смотрите ответы здесь: инициализация C struct с использованием меток. Это работает, но как?), можно инициализировать структуру следующим образом:
struct inventory_item {
int bananas;
int apples;
int pineapples;
};
inventory_item first_item = {
bananas: 2,
apples: 49,
pineapples: 4
};
Ответ 17
вы можете использовать extern "C" для компиляции кода c на С++. В вашем случае вы можете использовать ниже код
extern "C" {
//write your C code which you want to compile in C++
struct address {
int street_no;
char *street_name;`enter code here`
char *city;
char *prov;
char *postal_code;
};
address temp_address ={ .city = "Hamilton", .prov = "Ontario" };
}