Lets call your solution with some name? Something like
"intrusive tags"? Then it is easier to reason when things have
names. ;) Modern day often only profiler can tell for sure what
has advantage or disadvantage in fastness in concrete usage
situations. Thinking helps a bit but since compilers are quite
clever now it is often wrong. If it is faster then it can be used
as performance optimization. I don't see how it is simpler.
> Sometimes we have two or more types of objects inside the same
> container. It doesn't mean that these types have something
> in common. Maybe the only think they have in common is that
> they are used together.
> Lets say you have a container of 'car parts'. Them we have
> glass and tires. What do they have in common?
> In this case, using interfaces we may have IObject and
> the container vector<IObject>.
> But IObject is two generic. I can have glass or tires, but
> I cannot have fruit. If we had ICarPart then the glass or tire
> is very coupled with the original problem and is difficult to reuse
> and confuse as well.
We can use some problem domain oriented name like
"IPart" for possibly spare parts of different vehicles or
"IConsumable" for stuff that can be possibly eaten by
beings. But we will lose compile-time checking that
"wing" is unacceptable "part" for "car" or "hay" is
unacceptable "food" for "tiger" both with such
interfaces and with your intrusive tagging.
With visitor it is also likely run-time issue, with variant
there is concrete set of types so it is compile-time
issue.
> In the AST, one sample is static_assert it can be in different
> positions without any relationship with the other parts.
Two things are important: if it is easy to unit-test and
if less skilled programmers can take over its maintenance.
I feel that intrusive tagging can be unit-tested OK but it
takes yourself to be in control over it right now.
With AST-like problems I would go with variant anytime,
smaller types directly, bigger, polymorphic and/or recursive
types as (smart)pointer in variant. We can concentrate on
actual features and leave performance questions for later.
Code of std::variant is huge but it is mostly compile-time
meta-programming. The external tag of it is not such a
speed or storage issue and the compile-time type safety
defeats lot of issues with clumsy maintenance.
> I will consider this solution in many other cases,
> where I am the owner of the types. (I know all types)
Yes, I think the weakness is that you have to own the types.
With unknown, future types the options are always limited
to usage of abstract interface classes, clever compile-time
meta-programming, code generators, callbacks, lambdas
and/or std::function. But your intrusive tags can be useful
as performance optimization in implementation details.
Yes, in C it makes lot of sense since every data in C is POD
with certain binary layout. In C++ the intrusive tags are tricky
for two reasons:
1) Same binary location of tags in objects of different types is
hard to achieve. "Standard layout" is rather restrictive trait.
Otherwise it is undefined behavior. Having special purpose
interface to read the tag (like ITagged) would however defeat
most efficiency advantages. Also dynamic (cross?)casting to
what it apparently leads into can raise major violent
controversy (AKA shitstorm) in some collectives.
2) Difference between different tags and equality of tags
to same type in shared library and main program is hard
to achieve automatically. There are no "link-time counters"
or "link-time GUID" in C++. Linking is perhaps mentioned
in less than handful of non-normative comments of
standard. That issue leads either to language extensions
or to code generators.