user9400869
user9400869

Reputation:

How to test for typename in class?

In a class template Foo I want to check if the template parameter provides a type named Bar.

struct TypeA {using Bar = int;};
struct TypeB {};
template<class T>Foo{};

void main(){
  Foo<TypeA> a; // I want this to compile
  Foo<TypeB> b; // I want this to not compile, but print a nice message.
}

Since I want to combine this with other properties I want a hasBar metafunction. So I can combine the boolean values and then use std::enable_if.

I tried to understand and use SFINAE but failed:

template<class T, class Enable = void>struct hasBar : std::false_type {};

template<class T>
struct hasBar<T,decltype(std::declval<T::Bar>(),void())> : std::true_type {};

hasBar<TypeA>::value is always false.

What is the right way to define hasBar?

Or is there some better approach then using to have a Bar?

Upvotes: 2

Views: 61

Answers (2)

songyuanyao
songyuanyao

Reputation: 173004

You should add typename before T::Bar to specify that it's a nested type name, i.e.

template<class T>
struct hasBar<T,decltype(std::declval<typename T::Bar>(),void())> : std::true_type {};

LIVE

BTW: You can use std::void_t to make it simpler. e.g.

template< class, class = std::void_t<> >
struct hasBar : std::false_type { };

template< class T >
struct hasBar<T, std::void_t<typename T::Bar>> : std::true_type { };

Upvotes: 2

paler123
paler123

Reputation: 976

The simplest way would be to use the dependent type as the default value for an unnamed template parameter. Something like this:

struct with_bar { using Bar = int; };
struct without_bar { };

template <typename T, typename = typename T::Bar>
struct bar_detector { };

int main() {
  bar_detector<with_bar>();
  bar_detector<without_bar>(); // won' compile
}

This yields a pretty usable error message (g++ 7.3.0): error: no type named ‘Bar’ in ‘struct without_bar’.

Upvotes: 3

Related Questions