Удобная инициализация структуры C ++

117

Я пытаюсь найти удобный способ для инициализации '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 */ или лучше на С++? В качестве альтернативы, я был бы в порядке с объяснением, почему я не должен этого делать (то есть, почему моя психическая парадигма плоха).

ИЗМЕНИТЬ

Удобно, я имею в виду также поддерживаемое и не избыточное.

  • 2
    Я думаю, что пример Б так близко, как вы собираетесь получить.
  • 2
    Я не вижу, как пример B "плохой стиль". Это имеет смысл для меня, так как вы инициализируете каждого члена по очереди их соответствующими значениями.
Показать ещё 5 комментариев
Теги:
struct
initialization

8 ответов

34

Так как style A не разрешен в С++, и вы не хотите style B, то как насчет использования style BX:

FooBar fb = { /*.foo=*/ 12, /*.bar=*/ 3.4 };  // :)

По крайней мере, помогите в некоторой степени.

  • 8
    +1: на самом деле это не гарантирует правильную инициализацию (от POV компилятора), но, несомненно, помогает читателю ... хотя комментарии должны быть синхронизированы.
  • 15
    Комментарий не препятствует инициализации структуры, если я добавлю новое поле между foo и bar в будущем. C все равно инициализирует нужные нам поля, а C ++ - нет. И в этом суть вопроса - как добиться того же результата в C ++. Я имею в виду, Python делает это с именованными аргументами, C - с «именованными» полями, и у C ++ тоже должно быть что-то, я надеюсь.
Показать ещё 3 комментария
6

Ваш вопрос несколько затруднен, потому что даже функция:

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);

И вы можете перцем в защите продвижения по типу, если хотите.

  • 1
    «В C ++ то, что вы хотите, достижимо»: разве OP не просил помочь предотвратить смешивание порядка параметров? Как шаблон, который вы предлагаете, достиг бы этого? Просто для простоты, скажем, у нас есть 2 параметра, оба из которых int.
  • 0
    @max: Это предотвратит это, только если типы различаются (даже если они конвертируемы друг в друга), что является ситуацией OP. Если он не может различить типы, то, конечно, он не работает, но это совсем другой вопрос.
Показать ещё 1 комментарий
5

Если вы можете, используйте GCC. Его С++ front.end понимает синтаксис синтаксиса C.

  • 15
    Что не соответствует стандарту C ++!
  • 4
    Я знаю, что это нестандартно. Но если вы можете использовать его, это все еще самый разумный способ инициализации структуры.
Показать ещё 4 комментария
4

Еще один способ в С++ -

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);
  • 2
    Громоздко для функционального программирования (т.е. создания объекта в списке аргументов вызова функции), но в действительности это отличная идея!
  • 76
    -1. Это явно не initialization .
Показать ещё 6 комментариев
3

Извлеките контингенты в функции, которые их описывают (базовый рефакторинг):

FooBar fb = { foo(), bar() };

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

Еще одна вещь, которую вы могли бы сделать (поскольку вы ленивы) - сделать конструктор встроенным, поэтому вам не нужно вводить столько же (удаление "Foobar::" и время, затрачиваемое на переключение между h и cpp файлом):

struct FooBar {
  FooBar(int f, float b) : foo(f), bar(b) {}
  int foo;
  float bar;
};
  • 0
    Я настоятельно рекомендую всем, кто читает этот вопрос, выбрать стиль в нижнем фрагменте кода для этого ответа, если все, что вам нужно - это иметь возможность быстро инициализировать структуры с помощью набора значений.
3

Вариант 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, юридический С++. Именованные аргументы.

  • 16
    -1. Это явно не initialization .
  • 12
    Вместо memset вы можете использовать FooBar fb = {}; в C ++ он по умолчанию инициализирует все члены структуры.
Показать ещё 2 комментария
2

Как насчет этого синтаксиса?

typedef struct
{
    int a;
    short b;
}
ABCD;

ABCD abc = { abc.a = 5, abc.b = 7 };

Просто протестирован на Microsoft Visual С++ 2015 и на g++ 6.0.2. Работа в порядке.
Вы также можете создать конкретный макрос, если хотите избежать дублирования имени переменной.

  • 0
    clang++ 3.5.0-10 с -Weverything -std=c++1z кажется, подтверждает это. Но это не выглядит правильно. Знаете ли вы, где стандарт подтверждает, что это действительно C ++?
  • 0
    Я не знаю, но я использовал это в разных компиляторах с давних времен и не видел никаких проблем. Сейчас проверено на g ++ 4.4.7 - отлично работает.
Показать ещё 2 комментария
1

Способ /* B */ отлично работает на С++, и С++ 0x расширяет синтаксис, поэтому он также полезен для контейнеров С++. Я не понимаю, почему вы называете это плохим стилем?

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

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

  • 0
    Почему это хорошо?
  • 7
    Я называю это плохим стилем, потому что я думаю, что он не подлежит ремонту. Что если я добавлю еще одного участника в год? Или если я изменю порядок / типы участников? Каждый фрагмент кода, инициализирующий его, может (очень вероятно) сломаться.
Показать ещё 7 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню