Устойчивость initializer list к изменениям кода

Есть такой код:

#include <iostream>

struct Range {
    //bool enabled = false;
    //int index = 0;
    int begin = 0;
    int end = 0;
};

int main()
{
    Range r = { 2, 3 };
    std::cout << r.begin << " " << r.end;
}

Если я изменю состав структуры Range, раскомментировав одну из закомментированных строчек, то программа скомпилируется, но работать будет неправильно. Если добавить в начало Range поле типа bool, то компилятор хотя бы будет выдавать предупреждение. А если добавить в начало Range поле типа int, то даже предупреждений не будет.

Есть ли какой-то способ сделать так, чтобы при изменении состава структуры все initializer lists, инициализирующие её, стали невалидными или хотя бы выдавали предупреждение при компиляции? Ну или может быть есть какой-то другой надёжный способ найти все initializer lists в коде инициализирующие данную структуру?

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


Ответы (2 шт):

Автор решения: AlexGlebe

Для надёжности можно передать с именованными полями структуру так :

Range r = { . begin = 2 , . end = 3 } ;

В плюсах добавили в C++20 , в Си было в С99.


В английском варианте вопроса How to make initializer lists robust to code changes? предлагают сделать конструктор с новым количеством аргументов. Это даст вам возможность переписать везде код, добавляя новый аргумент. Выставлять аргументам значения по умолчанию не надо, а то не поможет.

struct Range {
    //bool enabled = false;
    int index = 0;
    int begin = 0;
    int end = 0;
  // новый конструктор, требующий три аргумента
  Range ( int i , int b , int e ) :
    index{i},begin{b},end{e}{}  
  // старый конструктор, требующий два аргумента
  Range ( int b , int e ) :
    begin{b},end{e}{}  
};

int main(){
  // старый код
  Range r = { 2, 3 };
  // новый код
  Range nr = { 1, 2, 3 };
}
→ Ссылка
Автор решения: αλεχολυτ

До тех пор, пока при инициализации отсутствует контроль по именам членов класса, могут появляться неоднозначности, особенно, когда типы членов совпадают или для них допустимы неявные преобразования. Варианты с именами рассмотрены в соседнем ответе: версия C++20 и конструктор (или конструкторы).

Но есть и другой подход. Если на текущий момент код валидный, и новые члены структуры добавляются только в конец, то проблем с агрегатной инициализацией не возникнет.

struct Range {
    int begin = 0;
    int end = 0;
    bool enabled = false;
    int index = 0;
};

Range r = { 2, 3 };

Хвостовые элементы, не инициализированные явно, будет инициализированы значениями по умолчанию.

→ Ссылка