0

Suppose I have a struct for data:

struct TestData {
}

and a class with a member variable:

class TestContainer {
private:
  TestData data;
};

Both are defined in a cpp file from a macro that is used in multiple test files.

Now I want to remove the data member at compile time if there is no TestData struct defined. If a test doesn't need data then there's no need to define the data member (and it would produce a warning for not being used). I thought of using std::enable_if but failed to come up with a condition. Another approach would be to define a base template class and specializations as shown in this question, but how to specialize on the existence of a type?

How can this be done?

Mike Lischke
  • 48,925
  • 16
  • 119
  • 181
  • [Duplicate](https://stackoverflow.com/q/53297795/3233393)? Although just disabling the warning sounds way easier and cleaner to me. – Quentin Mar 29 '19 at 16:04
  • If I understand correctly, the type `TestData` is sometime defined and sometime not? – Guillaume Racicot Mar 29 '19 at 16:06
  • Exactly, but I could also live with the idea of having an undefined vs. a defined type (by forward declaring TestData in the init section of the test). – Mike Lischke Mar 29 '19 at 16:19

1 Answers1

2

You can check if a struct exist or not if you consider a forward declaration is not existing.

This example assume that either TestData is always defined or never defined:

#include <type_traits>

// Comment this line to trigger the static assert
struct TestData {};

template<typename, typename = void>
struct MaybeData {};

template<typename T>
struct MaybeData<T, std::void_t<decltype(sizeof(T))>> {
    T data;
};

struct TestContainer : MaybeData<struct TestData> {};

We can test our solution like this:

template<typename, typename = void>
constexpr auto has_data = false;

template<typename T>
constexpr auto has_data<T, std::void_t<decltype(T::data)>> = true;

static_assert(has_data<TestContainer>);

The mechanism behind this is instead of sending the struct itself (TestData) as a type, we are using struct TestData as an argument, which refers to the type if it exist, but forward declare it if not.

Then, we use sfinae to check if sizeof(T) is a valid expression. If TestData is an imcomplete type, the expression is not valid.

However, if the completeness of a type changes between instantiation of the template, the program is ill formed.

Live example

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141