Хорошо, чтобы предоставить конструктор для агрегатов без поведения (bundle-o-data) в C++?
Пожалуйста, обратитесь к правилу № 41 C++ Standards Standards или Sutter's Gotw # 70, которое гласит:
Сделайте элементы данных частными, за исключением агрегатов без поведения (структуры в стиле C).
Я часто хотел бы добавить простой конструктор к этим структурам в стиле C, для удобства. Например:
struct Position
{
Position(double lat=0.0, double lon=0.0) : latitude(lat), longitude(lon) {}
double latitude;
double longitude;
};
void travelTo(Position pos) {...}
main()
{
travelTo(Position(12.34, 56.78));
}
Упрощая создание позиции на лету, конструктор также любезно инициализирует объекты позиции по умолчанию для меня.
Может быть, я могу последовать примеру std::pair и предоставить бесплатную функцию "makePosition"? NRVO должен сделать это так же быстро, как конструктор, верно?
Position makePosition(double lat, double lon)
{
Position p;
p.latitude = lat;
p.longitude = lon;
return p;
}
travelTo(makePosition(12.34, 56.78));
Я иду против духа концепции "совокупности без поведения", добавив этот жалкий маленький конструктор?
РЕДАКТИРОВАТЬ:
Да, я знал Position p={12.34, 56.78}
, Но я не могу сделать travelTo({12.34, 56.78})
с чистыми структурами С.
РЕДАКТИРОВАТЬ 2:
Для тех, кто интересуется типами POD: что такое типы POD в C++?
ПОСЛЕДУЮЩАЯ ДЕЯТЕЛЬНОСТЬ: Я задал здесь дополнительный вопрос, тесно связанный с этим.
5 ответов
Мы регулярно определяем конструкторы для наших типов агрегатов без каких-либо побочных эффектов. Фактически, я могу думать только о том, что в критических ситуациях вы не можете избежать инициализации по умолчанию и что вы не можете использовать тип в объединениях.
Альтернативы - стиль инициализации в фигурных скобках
Position p = {a,b};
или бесплатная функция "сделать"
Position makePosition(double a, double b)
{
Position p = {a,b};
return p;
}
проблема с первым заключается в том, что вы не можете использовать его для создания экземпляра временного объекта для передачи в функцию.
void func(Position p)
{
// ...
}
// func({a,b}) is an error
последнее прекрасно в этом случае, но для ленивого программиста это немного больше. Проблема с последней формой (функцией make) заключается в том, что она оставляет возможность забыть инициализировать структуру данных. Поскольку неинициализированные переменные вызывают у меня чувство дискомфорта, я предпочитаю определять конструктор для своих агрегатных типов.
Основная причина, по которой существует std::make_pair, на самом деле не по этой причине (std::pair имеет конструкторы), а на самом деле потому, что для вызова конструктора типа шаблона вы должны передать аргументы шаблона - что неудобно:
std::pair<int,int> func()
{
return std::pair<int,int>(1,2);
}
Наконец, в вашем примере вы должны по крайней мере сделать ваш конструктор явным
explicit Position(double lat=0.0, double lon=0.0)
в противном случае вы допускаете неявное приведение к позиции из двойного
Position p = 0.0;
что может привести к непреднамеренному поведению. Фактически, я бы определил два конструктора, один для инициализации нулями, а другой для инициализации двумя значениями, потому что конструкция Position, вероятно, не имеет большого смысла без широты и долготы.
Я обычно предоставляю структуры конструктору, без проблем. Однако, если конструктор "нетривиален", то структура больше не считается типом POD, и будут ограничения на то, что вы можете с ней делать. Если это проблема для вас (это никогда не было для меня), тогда, очевидно, стоит использовать функцию make_XXXX.
Обратите внимание, что без конструктора следующее уже делает то, что вам нужно:
int main()
{
Position pos = { 12.34, 56.78 };
travelTo(pos);
Position pos2 = {}; // zero initialises
travelTo(pos2);
}
Концептуально это нормально - вы не идете вразрез с духом "агрегата без поведения". Проблема в том, что структура больше не является типом POD, поэтому стандарт дает меньше гарантий относительно ее поведения, и ее нельзя сохранить в союз.
Вы рассматривали это вместо этого?
Position p = {12.34, 56.78};
С тех пор, как я опубликовал этот вопрос, меня укусили за определение конструкторов для моих агрегатов. Используя мой пример положения выше, GCC жалуется, когда я делаю это:
const Position pos[] =
{
{12.34, 56.78},
{23.45, 67.89},
};
предупреждение: расширенные списки инициализаторов доступны только с -std= C++0x или -std=gnu++0x|
Вместо этого я должен сделать это:
const Position pos[] =
{
Position(12.34, 56.78),
Position(23.45, 67.89)
};
С этим решением я беспокоюсь, что во встроенных системах моя постоянная таблица не будет храниться во флэш /ROM.
РЕДАКТИРОВАТЬ:
Я попытался удалить конструктор Position, и массив pos действительно переместился из.bss в сегмент.rodata.