Устойчивость 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 шт):
Для надёжности можно передать с именованными полями структуру так :
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 };
Хвостовые элементы, не инициализированные явно, будет инициализированы значениями по умолчанию.