Ok to provide constructor for behaviorless aggregates (bundle-o-data) in C++?

c++

Solution

We regularly define constructors for our aggregate types, with no adverse effects. In fact the only adverse effects I can think of are that in performance critical situations you cannot avoid default initialisation and that you can't use the type in unions.

The alternatives are the curly brace style of initialisation

Position p = {a,b};

or a free "make" function

Position makePosition(double a, double b)
{
    Position p = {a,b};
    return p;
}

the problem with the former is that you can't use it to instantiate a temporary to pass into a function

void func(Position p)
{
    // ...
}

// func({a,b}) is an error

the latter is fine in this case, but is very slightly more typing for the lazy programmer. The problem with the latter form (a make function) is that it leaves the possibility that you forget to initialise your data structure. Because uninitialised variables leave me feeling rather uncomfortable I prefer to define a constructor for my aggregate types.

The main reason std::make_pair exists is actually not for this reason (std::pair has constructors), but in fact because to call the constructor of a template type you have to pass the template arguments - which is inconvenient:

std::pair<int,int> func()
{
    return std::pair<int,int>(1,2);
}

Finally, in your example, you should at least make your constructor explicit

explicit Position(double lat=0.0, double lon=0.0)

otherwise you allow an implicit cast to a Position from a double

Position p = 0.0;

which might be lead to unintended behaviour. In fact I would define two constructors, one to initialise to zero and one to initialise with two values because the Position construct probably doesn't make much sense without both a latitude and a longitude.

Problem

Please refer to rule #41 of C++ Coding Standards or Sutter's Gotw #70, which states that: Make data members private, except in behaviorless aggregates (C-style structs). I often would like to to add a simple constructor to these C-style structs, for the sake of convenience. For example: ``` 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)); } ``` While making it easier to construct a Position on the fly, the constructor also kindly zero-initializes default Position objects for me. Maybe I can follow std::pair's example and provide a "makePosition" free function? NRVO should make it as fast as the constructor, right? ``` Position makePosition(double lat, double lon) { Position p; p.latitude = lat; p.longitude = lon; return p; } travelTo(makePosition(12.34, 56.78)); ``` Am I going against the spirit of the "behaviorless aggregate" concept by adding that measly little constructor? EDIT: Yes, I was aware of `Position p={12.34, 56.78}`. But I can't do `travelTo({12.34, 56.78})` with pure C structs. EDIT 2: For those curious about POD types: What are POD types in C++? FOLLOW-UP: I've asked a follow-up question here that is closely related to this one.

Original source

Related problems