[variant] Warning: variadic templates in boost::variant

Development version of Boost.Variant library now uses C++11 variadic
templates when possible.
This leads to more compact binaries, more readable compiler error messages
and faster compilation times.
However this:
* breaks ABI (now variant is declared as `template

Antony Polukhin вроте:
However this: * breaks ABI (now variant is declared as `template
class variant`)
Why not just template

On Monday 09 December 2013 14:02:12 Peter Dimov wrote:
What should be the storage for such a variant then? Also, most of the interface of variant<> becomes invalid (e.g. type(), which(), assignment from elements, apply_visitor()). With such drastic interface differences, there should be a compelling reason to have such a specialization, and I don't see one.

2013/12/9 Peter Dimov
I can easily add `template <> class variantboost::mpl::void_ {}; ` and `template <> class variant<> {};` class specializations that have no methods. Somebody needs them? -- Best regards, Antony Polukhin

AMDG On 12/09/2013 04:16 AM, Andrey Semashev wrote:
Boost.Variant explicitly guarantees that it always holds one of the variant types. variant<> doesn't really make sense, because it's impossible to satisfy this invariant. variant<X> isn't very useful, but it does have a sensible meaning. In Christ, Steven Watanabe

On 12/09/13 08:52, Steven Watanabe wrote: [snip]> variant<> doesn't
really make sense, because it's impossible to satisfy this invariant.
I'm not as sure variant<> doesn't make some sense. tuple<> makes sense, at least according to: http://en.wikipedia.org/wiki/Tuple and variant<> is a "dual" to tuple: http://en.wikipedia.org/wiki/Dual_%28category_theory%29 Also the following: http://en.wikipedia.org/wiki/Coproduct says: The coproduct indexed by the empty set (that is, an empty coproduct) is the same as an initial object in C. where C is some category and "coproduct", IIUC, is what variant is (the Coproduct page mentions it at a "disjoint union of sets"). Hence, at least the Coproduct page sees some merit in defining an "empty coproduct" or variant<>. -regards, Larry

Steven Watanabe wrote:
The difference between tuple<> and variant<> is that tuple<> has exactly one possible value, but variant<> has no possible values.
Not conceptually. tuple

On Mon, Dec 9, 2013 at 12:28 PM, Peter Dimov
I'm going to have to back you up here that a variant<> does sometimes make sense and I don't think we'd lose anything by supporting it. In real, non-hypohetical code, I've encountered a place where I had a 0-element variant come up and had to special-case my code so that it would work as expected. The way it came up was I had a variant of several types (the type list was the result of some metaprogramming) and a visitor of that variant that returned the result of a member function call on the currently stored object, whichever one it was (each type in the variant had the same named function but the return type wasn't necessarily the same). I constructed the return type based on the return types of the functions in question, eliminating any duplicates. If one of the functions returned a void type, no type was added to the list. I stored this result and later on did things with it (such as display it). What this meant in practice was that I was sometimes left with 0-element and 1-element variants. It would be nice if the 0 case just worked as expected rather than having to special-case. As well, in the case of a single-element variant, it would be nice if the ID weren't explicitly stored since it would always be the same value (I don't remember if Boost.Variant makes this optimization). If there is some ambiguity with how a 0-element variant should be handled, then I'd say perhaps we should leave it as a compile-time error, but I think that in practice you'd just expect it to be a stateless type that either always does nothing when visited, passes some kind of special tag type when visited (this might be better since it would work with n-ary visitation), or simply produces a compile-time error when visited. I don't know what this means with respect to Variant's "never-empty" guarantee or the result of .empty(), but intuitively I'd just say that a variant satisfies the never-empty guarantee iff the amount of types it is over is greater than 0. In the 0 case, empty() would return false (it should also be static constexpr in any case). -- -Matt Calabrese

On 12/09/13 22:36, Matt Calabrese wrote:> On Mon, Dec 9, 2013 at 12:28
PM, Peter Dimov
Steven Watanabe wrote:
The difference between tuple<> and variant<> is that tuple<> has
exactly things
Such a type of "nullable" variant<> exists as
container

On Tue, Dec 10, 2013 at 5:54 AM, Andrey Semashev
While that is the current workaround, I look at it as exactly that -- a work-around. It just seems very arbitrary to me and only makes it so that people have to special-case for a logical variant<> in metaprogrammed code. As Peter Dimov also pointed out, variant<> seems to be just as valid as a union {}, and I think most people would expect it to work pretty much the same way. I also don't think we'd lose anything by simply supporting it and the implementation would be trivial. -- -Matt Calabrese

On Tuesday 10 December 2013 09:32:11 Matt Calabrese wrote:
The problem is that, as opposed to union, variant provides advanced APIs which loose their meaning if no type is specified. Making variant<> a special case with its own set of APIs makes generic programming more difficult since you have to propagate support for that special case through all surrounding templates. And on top of that the special case adds variant implementation complexity for little-to-no outcome. If variant<> is really desired, it has to support all the APIs that are defined now for the non-empty variant. You can achieve that by making it always non-empty with a reasonable default, e.g.: template< typename T0 = blank, typename... TN > class variant;

On Tue, Dec 10, 2013 at 10:43 AM, Andrey Semashev
wrote:
While that's true currently, I think that the issue is just the way in which we have specified the requirements of a variant. In other words, it was a design choice made early on because there wasn't a clear use-case for variant<>. IMO, a variant<> is, as Peter also pointed out, analogous to union {}, and I don't see anything logically wrong with such a construct. I've also encountered the issue in practice so there is at least one use-case.
I'm not sure I agree. It wouldn't have its own set of APIs. At most, it would just be a subset of variant functionality (I.E. which() might not be defined, or perhaps better, would be declared but either not be defined or would static_assert if the definition were instantiated). If code you're writing doesn't need to deal with the empty variant case, none of this should be impacting you, you just personally have the requirement that the number of types is >= 1 in your generic code, but for people that do need to deal with it, they'd simply be using the subset of the API that makes sense in their generic code. At this point in time, variant<> is just a compile-error at the library-level, so users have to special-case if some metaprogramming happens to results in a variant<>, regardless of what trivial things they may attempt to do with it. Directly supporting variant<> in a way that makes sense would eliminate the need for users to do low-level special-casing. Again, this isn't hypothetical. In practice I have encountered uses and the existence of a variant<> would have just worked had it existed. For instance, in the case I described in an earlier email, I had a "display" visitor that displayed the contained object. In the empty case it made sense for me to just do nothing. If variant<> were defined and apply_visitor simply passed in an instance of some null tag type, it would have been trivial for me to accomplish that without doing any special-casing for the creation of the variant -- my visitor could have just been defined to do nothing in that case. I wouldn't have had to special case the creation of the variant. Further, I don't see how this use-case is at-all unique and I wouldn't be surprised if others have encountered situations where metaprogramming results in a variant<>. Manually passing in a tag type when creating the variant in the case that metaprogramming results in an empty sequence seems like a code smell to me. If variant<> is really desired, it has to support all the APIs that are
Something like that may very well end up being the simplest solution and I'd be in favor of a simple change such as that over the current state of having variant<> be a compile error (similarly, make_variant_over with an empty sequence should work...). I still think supporting an /actually/ empty variant might be better, but as long as there is some way to logically represent variant<> and deal with it in generic code I'd personally be satisfied. -- -Matt Calabrese

On 12/10/13 14:32, Matt Calabrese wrote:
[snip] Hi Matt, I'm trying to understand how you arrive at the static_visitor. Since the set of bounded types in the variant is a result of metaprogramming, I'm guessing that the static_visitor would also have to be the result of some metaprogramming. Otherwise I can't figure out how it could be done. I'm guessing that you use some sort of inheritance for each bounded type. Is that right? -regards, Larry

On 12/10/13 15:35, Larry Evans wrote: [snip]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Should be:
inheritance for the operator() member function
for each bounded type.
IOW.
template

On Tue, Dec 10, 2013 at 1:35 PM, Larry Evans
The visitor is not metaprogrammed. The "display" visitor is used for generic visualization of the variant, regardless of what's inside. It simply has an operator() template that uses a display function found by ADL (much like operator<< would be if we were doing generic output to a stream). If the variant is just variant<>, it makes sense to not display anything in this particular case. This would all be trivial to do if variant<> just worked and we specified what happens when you use apply_visitor on it.
I'm guessing that you use some sort of inheritance for each bounded type. Is that right?
No. -- -Matt Calabrese
participants (6)
-
Andrey Semashev
-
Antony Polukhin
-
Larry Evans
-
Matt Calabrese
-
Peter Dimov
-
Steven Watanabe