Я пытаюсь найти удобный способ для инициализации 'pod' С++ structs. Теперь рассмотрим следующую структуру:
struct FooBar {
int foo;
float bar;
};
// just to make all examples work in C and C++:
typedef struct FooBar FooBar;
Если я хочу удобно инициализировать это в C (!), я мог бы просто написать:
/* A */ FooBar fb = { .foo = 12, .bar = 3.4 }; // illegal C++, legal C
Обратите внимание, что я хочу явно избегать следующих обозначений, потому что мне кажется, что мне нужно сломать шею, если я изменю что-либо в структуре в будущем:
/* B */ FooBar fb = { 12, 3.4 }; // legal C++, legal C, bad style?
Чтобы достичь того же (или, по крайней мере, подобного) в С++, как в примере /* A */
, мне пришлось бы реализовать идиотский конструктор:
FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar) {}
// ->
/* C */ FooBar fb(12, 3.4);
Это хорошо для кипящей воды, но не подходит для ленивых людей (лень - это хорошо, правда?). Кроме того, это очень плохо, как пример /* B */
, поскольку он явно не указывает, какое значение переходит к тому элементу.
Итак, мой вопрос в основном заключается в том, как я могу достичь чего-то похожего на /* A */
или лучше на С++?
В качестве альтернативы, я был бы в порядке с объяснением, почему я не должен этого делать (то есть, почему моя психическая парадигма плоха).
ИЗМЕНИТЬ
Удобно, я имею в виду также поддерживаемое и не избыточное.
Так как style A
не разрешен в С++, и вы не хотите style B
, то как насчет использования style BX
:
FooBar fb = { /*.foo=*/ 12, /*.bar=*/ 3.4 }; // :)
По крайней мере, помогите в некоторой степени.
foo
и bar
в будущем. C все равно инициализирует нужные нам поля, а C ++ - нет. И в этом суть вопроса - как добиться того же результата в C ++. Я имею в виду, Python делает это с именованными аргументами, C - с «именованными» полями, и у C ++ тоже должно быть что-то, я надеюсь.
Ваш вопрос несколько затруднен, потому что даже функция:
static FooBar MakeFooBar(int foo, float bar);
можно назвать следующим:
FooBar fb = MakeFooBar(3.4, 5);
из-за правил продвижения и конверсий для встроенных числовых типов. (C никогда не был действительно строго типизирован)
В С++ то, что вы хотите, возможно, но с помощью шаблонов и статических утверждений:
template <typename Integer, typename Real>
FooBar MakeFooBar(Integer foo, Real bar) {
static_assert(std::is_same<Integer, int>::value, "foo should be of type int");
static_assert(std::is_same<Real, float>::value, "bar should be of type float");
return { foo, bar };
}
В C вы можете назвать параметры, но вы никогда не получите больше.
С другой стороны, если все, что вам нужно, это именованные параметры, тогда вы пишете много громоздкого кода:
struct FooBarMaker {
FooBarMaker(int f): _f(f) {}
FooBar Bar(float b) const { return FooBar(_f, b); }
int _f;
};
static FooBarMaker Foo(int f) { return FooBarMaker(f); }
// Usage
FooBar fb = Foo(5).Bar(3.4);
И вы можете перцем в защите продвижения по типу, если хотите.
Если вы можете, используйте GCC. Его С++ front.end понимает синтаксис синтаксиса C.
Еще один способ в С++ -
struct Point
{
private:
int x;
int y;
public:
Point& setX(int xIn) { x = Xin; return *this;}
Point& setY(int yIn) { y = Yin; return *this;}
}
Point pt;
pt.setX(20).setY(20);
initialization
.
Извлеките контингенты в функции, которые их описывают (базовый рефакторинг):
FooBar fb = { foo(), bar() };
Я знаю, что стиль очень близок к тому, который вы не хотели использовать, но он позволяет легче заменять постоянные значения, а также объяснять им (при этом не нужно редактировать комментарии), если они когда-либо меняются.
Еще одна вещь, которую вы могли бы сделать (поскольку вы ленивы) - сделать конструктор встроенным, поэтому вам не нужно вводить столько же (удаление "Foobar::" и время, затрачиваемое на переключение между h и cpp файлом):
struct FooBar {
FooBar(int f, float b) : foo(f), bar(b) {}
int foo;
float bar;
};
Вариант D:
FooBar FooBarMake(int foo, float bar)
Юридический C, юридический С++. Легко оптимизируется для POD. Конечно, нет именованных аргументов, но это похоже на все С++. Если вам нужны именованные аргументы, Objective C должен быть лучшим выбором.
Вариант E:
FooBar fb;
memset(&fb, 0, sizeof(FooBar));
fb.foo = 4;
fb.bar = 15.5f;
Юридический C, юридический С++. Именованные аргументы.
initialization
.
FooBar fb = {};
в C ++ он по умолчанию инициализирует все члены структуры.
Как насчет этого синтаксиса?
typedef struct
{
int a;
short b;
}
ABCD;
ABCD abc = { abc.a = 5, abc.b = 7 };
Просто протестирован на Microsoft Visual С++ 2015 и на g++ 6.0.2. Работа в порядке.
Вы также можете создать конкретный макрос, если хотите избежать дублирования имени переменной.
clang++
3.5.0-10 с -Weverything -std=c++1z
кажется, подтверждает это. Но это не выглядит правильно. Знаете ли вы, где стандарт подтверждает, что это действительно C ++?
Способ /* B */
отлично работает на С++, и С++ 0x расширяет синтаксис, поэтому он также полезен для контейнеров С++. Я не понимаю, почему вы называете это плохим стилем?
Если вы хотите указать параметры с именами, вы можете использовать библиотеку параметров форматирования, но это может смутить незнакомого с ней человека.
Переупорядочивание элементов структуры похоже на параметры функции переупорядочения, такой рефакторинг может вызвать проблемы, если вы не делаете это очень осторожно.