0

Is it possible to have a struct which may or may not have a member? Something like this:

template <typename T, typename A = some_type_with_size_0>
struct s {
    T t;
    A aux;
};

To be specific, if I asked for s<int, int> I would get a struct with two ints, but if I asked for s<int> I would get a struct with only an int.

Marco Merlini
  • 875
  • 7
  • 29
  • 1
    Sounds a bit like an XY-problem, can you elaborate your use case more please. `some_type_with_size_0` won't prevent to instantiate an `aux` member, whatever type that should be (`void` won't work either IIRC). – πάντα ῥεῖ Nov 13 '18 at 22:30
  • Related: https://stackoverflow.com/questions/10395707/depending-on-a-class-template-parameter-define-or-not-define-a-function-in-the – πάντα ῥεῖ Nov 13 '18 at 22:33
  • if you want either one or two `int`s you could use a `std::vector` ;) seriously, I think it would help a lot if you give an tiny example of what you need that for – 463035818_is_not_an_ai Nov 13 '18 at 22:35

2 Answers2

1

In C++20, it will be possible to do what you're trying to do directly:

template <typename T, typename A = some_type_with_size_0>
struct s {
    T t;
    [[no_unique_address]] A aux;
};

See https://en.cppreference.com/w/cpp/language/attributes/no_unique_address.

In C++17, there's no straightforward way to specify a member that conditionally disappears. You need to write a full-blown partial specialization, like so:

template <typename T, typename A = void>
struct s {
    T t;
    A aux;
};
template <typename T>
struct s<T, void> {
    T t;
};

This unfortunately requires you to repeat yourself in typing out all the common members (in this case only t). To avoid this, we can stick the conditionally present members in a base class:

template <typename T, typename A = void>
struct s : optional_aux<A> {
    T t;
};
template <typename A>
struct optional_aux {
    A aux;
};
template <>
struct optional_aux<void> { };

In the case where A = void, this base class is empty, so the compiler has discretion to remove it entirely, making sizeof(s<T, void>) potentially equal to sizeof(T). The [[no_unique_address]] attribute basically makes empty base class optimization available for members as well.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • 1
    I'd maybe note that in the C++20 code, `s` still has a member named `aux`, which might matter for name hiding/visibility or for other templates' expression SFINAE. It's just that `sizeof(s)` is allowed to be smaller. – aschepler Nov 13 '18 at 22:49
  • @aschepler You're right. I read between the lines and assumed the OP is ok with the member existing as long as it takes up space. – Brian Bi Nov 13 '18 at 23:03
0

You can use a variadic template:

template <typename...> struct Generic;

template <typename T1> struct Generic<T1> {
  T1 field1;
};

template <typename T1, typename T2> struct Generic<T1, T2> {
  T1 field1;
  T2 field2;
};
Adam Kotwasinski
  • 4,377
  • 3
  • 17
  • 40