4 ms·
The big problem (other than verbose code) with rolling up stuff into classes in C++ is that performance suffers. Many platforms will not pass small structs effi
by clord 13y ago
The big problem (other than verbose code) with rolling up stuff into classes in C++ is that performance suffers. Many platforms will not pass small structs efficiently.
Inspired by Haskell's newtype, I drafted a proposal for C++14 which would have introduced native newtype to C++, but it got rolled into an omnibus paper (n3635) and appears to have been forgotten.
The proposal is modelled on strong enums, but generalizes them to all types. They are essentially strong typedefs with none of the weaknesses of wrapper types, restrict pointers, typedefs, and pragma disjoint.
newtype NotInt : int;
NotInt k(5); // call ‘default’
NotInt bar(NotInt);
bar(k); // passes exactly as an ABI int would
int baz(int&);
baz(k); // Error! no conversion
While drafting this proposal, I realized it was a much more C++-like way of bifurcating aliases than 'restrict' keyword:
newtype D1 : double;
newtype D2 : double;
void biz(D1 *x, D2 *y) {
// Being separate, D1/D2 can be
// assumed not to alias by optimizers
...
}
While playing with it, we came up with this proposed syntax (although I don't think this would get through the committee):
[template <typename T>]
newtype [NotT] : [(public|private)] T [= (default|explicit|deleted)] [{
// only non-virtual member functions, no data or reference members (something like POD)
// this->value has all of the operations of T, but only aliases with the new type, and implicitly converts to/from T prvalues.
}] [optional_variable_name];
- gordaco 13y agoI don't think there's going to be any performance difference at all compared to the basic case, which actually is another reason why this is so good. Ultimately, all these struct just hold a double, which means that in memory they're exactly a double and nothing more, there's no type tag or anything like that. And all the code is static so there is no vtable, just the double value. The compiler does the rest, not the runtime. (Still, I'd love to see newtypes in C++)
- comex 13y agoIt can make a difference due to weird ABIs. For example, in the ARM procedure call standard, 64-bit values can be returned in r0 and r1, but 64-bit structures can't (no matter whether they contain two 32-bit values or just one 64-bit value). However, I'd call that fairly niche - it's hard to imagine an application that would notice a significant performance difference.
- com2kid 13y agoThis is very nice to know actually, and quite relevant to code I am working on right now! Disappointing though. > it's hard to imagine an application that would notice a significant performance difference. One hopes any function call made often enough to be a perf hit from this would also be designed to be easily inlined!
- clord 13y agoScientific computing will hit this sort of problem just because the kernel is so large it actually needs to be broken into sub-procedures for icache reasons. But also, the penalty is large, because there is a full store/load cycle involved, which will clear caches and increase memory bandwidth requirements.