-
Notifications
You must be signed in to change notification settings - Fork 29
Knowledgebase Programming: Trivial objects
From discussion on Discord
"Primitive" doesn't exist in C++. "Arithmetic types" are like bool, double, int
. "Fundamental types" include arithmetics, void
and nullptr_t
. "Scalars" include arithmetics, nullptr_t
and pointers.
All scalars are trivial, but some non-scalars are also trivial. There is no "quick" way to see if a class is trivial. You need to look at its member variables, and its copy ctor, operator=
, and dtor. If the member vars are all trivial, and the copy ctor, operator=
and dtor are all =default
then the class is trivial. You can also check:
static_assert(std::is_trivial{}, "blah");
For me, trivial is an "easy" way to say whether a class doesn't need to be passed by reference. Other trivial classes include QStringView
and QLatin1String
.
The performance cost of pass-by-reference is that the compiler has fewer optimization opportunities, because it has to assume that the value could be changed by external code.
void throwBall(double value, const CLengthUnit &unit)
{
hand.hold(ball);
arm.prepareThrow(value, unit); // here the value of unit is accessed from memory
//...
logMessage();
// if logMessage is defined in another cpp file, the compiler
// must assume that it could modify the value of unit
arm.start(value, unit); // here the value of unit must be accessed from memory again,
// the compiler can't reuse the value from the previous access
}
Because it's possible that throwBall
could be called like this sneaky code:
CLengthUnit unit = /*...*/;
void logMessage() { unit = otherUnit; }
throwBall(10, unit);
When it's passed by value, the parameter is a local variable, so the compiler can see that it's not modified, and only needs to access the memory once:
void throwBall(double value, CLengthUnit unit)
{
hand.hold(ball);
arm.prepareThrow(value, unit); // here the value of unit is accessed from memory
//...
logMessage();
arm.start(value, unit); // here the compiler can just reuse the value from the previous access
}
Return values are a bit different, but I would need to have a "good reason" to use references anywhere. CMeasurementUnit
contains just a single non-owning pointer to const data, so it easily fits in a CPU register like any arithmetic type. operator==
hasn't been changed in ages. You might be thinking of convertFrom
.
Either way, comparing m_data == other.m_data
pointers is just as good as comparing this == &other
pointers.
Are our normal PQs trivial?
- Yes, actually. Whether they are "small" is another question. CPhysicalQuantity contains just a double and a CMeasurementUnit (which contains a pointer). So it's 128 bits (16 bytes). Could fit in an SSE2 register, so probably "small". But it's debatable. And some of our derived PQs add more members, like magnetic/true for headings, datum for altitudes.
- Actually IMHO the most important thing in all of this is that the
CMeasurementUnit::Data
instances beconstexpr
as it means they are initialized at compile-time, so no static order fiasco. And only "literal types" are candidates forconstexpr
, which is yet another category of type
- Home
- API documentation (Doxygen generated)
- Future of swift
- Style and Coding Standard
- Release Checklist
- Build swift
- Run swift as a developer
- Knowledgebase
- External resources
- Open Research Questions
- Aviation
- Programming
- Simulation
- Architecture