Consider the following snippet, as if written in a header file:
struct Foo {
// ...
};
template <class... Args>
Foo makeFoo(Args &&... args) {
return {std::forward<Args>(args)...};
}
I can call makeFoo
with some parameters, and get back a Foo
. Great.
Now what I want to do is replace some of makeFoo
's arguments with tags, which look just like this (still in the header file):
constexpr struct tag_type { using type = ActualType; } tag;
These tags should be detected inside makeFoo
and substituted for actual objects before calling Foo
's constructor. So the call would look like:
auto myFoo = makeFoo("hi", ::tagBar, 42, ::tagBaz);
But here's the catch: this way of declaring my tags is super convenient, but if I ODR-use any of them, I need to provide a definition somewhere else. Not convenient at all.
And according to this conveniently specific answer (emphasis mine):
"the object isn't odr-used" is probably the only questionable condition. Basically, it requires that you don't necessitate the variables runtime existence as a symbol, which in turn implies that
- You don't bind it to a reference (=> you don't forward it!)
- [...]
... I'm in a pickle indeed.
How can I sift out the tags from the arguments without ODR-using them, while perfect-forwarding the other arguments?
A "tag" is defined as something that has a
type
typedef.Each tag declaration is generated by a macro
defineTag(name, ActualType)
, so changing that is fine as long as it is self-contained and doesn't alter (too much) the syntax of the call tomakeFoo
.An alternate solution that isn't concerned with ODR at all is fine.
C++17's inline variables sound like salvation, but I'd like to avoid locking myself into bleeding-edge compilers on this project for that single issue.