Что такое эквивалент С++ для C инициализаторов?

Вы можете объявить структуру в C так:

typedef struct MyStruct {
    const char *name;
    int (*func1)(void);
    int (*func2)(void);
    int (*func3)(void);
} MyStruct;

int test_func2(void) {
    return 0;
}

MyStruct test_struct = {
    .name   = "buffer",
    .func2  = test_func2,
};

Это очень удобно для определения только определенных членов, а для остальных - 0/NULL.

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

Это не будет компилироваться вместе с компилятором С++, получив ошибку:

test.c:23: error: expected primary-expression before ‘.’ token
test.c:24: error: expected primary-expression before ‘.’ token

Существует ли эквивалентная декларация С++ для достижения того же?

Спасибо.

Изменить: @chris Я могу сказать, что вы не понимаете:) И совершенно очевидно, что большинство других людей, комментирующих, какой синтаксис я должен был использовать, как должна была быть определена структура и т.д., Полностью пропустили эту точку зрения. Речь идет не о правильном способе определения структуры, этот фрагмент был только там, чтобы обеспечить контекст.

Что касается кодовой эквивалентности, Скажите где-нибудь в вашем коде, который вы сделали:

MyStruct blah = { NULL, NULL, func2 };

Теперь MyStruct изменит его определение как:

typedef struct MyStruct {
  const char *name;
  int (*func4)(void);
  int (*func1)(void);
  int (*func2)(void);
  int (*func3)(void);
} MyStruct;

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

Вопрос был о том, есть ли инициализаторы на С++, эквивалентные: нет. проблема закрыта.

Ответ 1

Нет, С++ не поддерживает инициализаторы, назначенные C99. Если вы хотите установить отдельные члены по имени, вам нужно сделать это через назначение, например.

MyStruct test_struct = MyStruct();
test_struct.name  = "buffer";
test_struct.func1 = test_func1;

Ответ 2

Как сказал K-ballo, код эквивалентен:

MyStruct test_struct = {
    "buffer",
    test_func1
};

Но так как вы используете С++, вам следует написать и использовать конструкторы для MyStruct:

struct MyStruct {
    const char *name;
    int (*func1)(void);
    int (*func2)(void);
    int (*func3)(void);

    MyStruct(const char *n,
        int (*f1)(void) = NULL,
        int (*f2)(void) = NULL,
        int (*f3)(void) = NULL) : name(n), func1(f1), func2(f2), func3(f3) {}
};

Обратите внимание, что в С++ вам не нужно вводить его в MyStruct, вы можете просто использовать MyStruct (без ключевого слова struct) даже без typedef.

Ответ 3

Вы можете просто использовать лямбда (начиная с С++ 11). Это позволит вам оставить декларацию нетронутой, а также легко инициализировать глобальный/постоянный экземпляр вашей структуры:

struct Test
{
    int x;
    float y;
};

const Test test=[]{ Test t{}; t.x=5, t.y=3.3465; return t; }();

#include <iostream>
int main()
{
    std::cout << "test.x: " << test.x << ", test.y: " << test.y << "\n";
}

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

// Macro to simplify it a bit
#define DECL_AND_INIT(Type,object,INIT1,INIT2) \
Type object=[]{ Type o{}; o.INIT1; o.INIT2; return o; }()
// Example usage
const DECL_AND_INIT(Test,test1,x=-7,y=-9.325e31);

Обратите внимание, что вам все равно нужно использовать инициализацию значения, например Test t{}; в лямбда - иначе, если вы инициализируете не всех членов (например, из-за добавления новых), вы в конечном итоге скопируете неинициализированные переменные, ведущие к UB.

Ответ 4

Функциональная эквивалентность этого будет конструктором для вашего struct с пустым телом:

#ifdef __cplusplus
MyStruct::MyStruct(char* N, int (*F)(void)) :
    name(N),
    func1(0),
    func2(0),
    func3(F) {
  // empty
}
#endif

(по модулю некоторых синтаксических ошибок, мой С++ немного ржавый.)

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