1

I'm trying to create an variant struct, i.e. a struct that contains one of so many types. Here is my attempt so far:

template <typename Type, typename... Rest> struct OneOf {
    union {
        Type value;
        OneOf<Rest...> rest;
    };
};

template <typename Type> struct OneOf {
    Type value;
};

Sadly, this doesn't compile. When I try to instantiate it, I get:

one_of.h:34:33: error: redeclared with 1 template parameter template struct OneOf {

Is there a way to terminate a self referencing recursion with structs?

Shachar Shemesh
  • 8,193
  • 6
  • 25
  • 57

3 Answers3

2

You have to first declare the primary template, and then declare any specializations (either full or partial). The primary template determines the number and kind of template arguments. When it comes time to instantiate the template, a full specialization will be used if it matches exactly, or the best-matching partial specialization if any match, otherwise, the primary template will be instantiated.

If you want OneOf to be a template that takes any number of type template arguments (0 or more), then you should declare the primary template accordingly:

template <class... T> struct OneOf;

Then you'll need two specializations: one for the base case of the recursion, which can be taken to be the empty pack:

template <>
struct OneOf<> {};

and one for the recursive case, with at least one template parameter:

template <typename Type, typename... Rest> struct OneOf<Type, Rest...> {
    union {
        Type value;
        OneOf<Rest...> rest;
    };
};

Notice that both full and partial specializations require a template argument list after the template name. If you omit this, the compiler will think you are redeclaring the primary template, which causes the error you're seeing.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • I'm actually quite fine with the form I wrote (where zero types are not acceptable). Otherwise, this is definitely the most complete answer. – Shachar Shemesh Oct 18 '18 at 21:06
  • @ShacharShemesh Fair enough---then you can declare the primary template so that it takes 1 type or more, the base case to take exactly 1 type, and the recursive case to take 2 or more types. It's a little more verbose that way. – Brian Bi Oct 18 '18 at 21:07
1

I assume you are trying to write a specialization.

This is the syntax:

template <typename Type> struct OneOf<Type> {
//                                   ^~~~~~
    Type value;
};
bolov
  • 72,283
  • 15
  • 145
  • 224
  • It works, but I don't understand it. Can you elaborate on why the `` is needed, and why it isn't needed for the first form? – Shachar Shemesh Oct 18 '18 at 21:02
  • Just search for template specialization. Here are some starting points: https://en.cppreference.com/w/cpp/language/template_specialization and https://en.cppreference.com/w/cpp/language/partial_specialization – bolov Oct 18 '18 at 21:03
0

What about

template <typename...>
struct OneOf;

template <typename Type, typename... Rest>
struct OneOf<Type, Rest...> {
    union {
        Type value;
        OneOf<Rest...> rest;
    };
};

template <>
struct OneOf<> {
};

?

O also

template <typename, typename...>
struct OneOf;

template <typename T0, typename T1, typename ... Ts>
struct OneOf<T0, T1, Ts...> {
    union {
        T0 value;
        OneOf<T1, Rest...> rest;
    };
};

template <typename T0>
struct OneOf<T0> {
    T0 value;
};

?

max66
  • 65,235
  • 10
  • 71
  • 111