I have a template struct with a nested template struct.
template <int F>
struct foo
{
template <int B>
struct bar {
static constexpr int f = F;
static constexpr int b = B;
};
};
I want to create a trait like
template <class>
struct is_foo_bar : std::false_type { };
template <int F, int B>
struct is_foo_bar< foo<F>::bar<B> > : std::true_type { };
static_assert(is_foo_bar< foo<1>::bar<2> >::value);
This gives an error:
type/value mismatch at argument 1 in template parameter list for ‘template<class> struct is_foo_bar’<br>
struct is_foo_bar<foo<F>::bar<B>> : std::true_type { };
If I take bar
out like
template <int F, int B>
struct foo_bar {
static constexpr int f = F;
static constexpr int b = B;
};
template <int F>
struct foo
{
template <int B>
using bar = foo_bar<F, B>;
};
template <class>
struct is_foo_bar : std::false_type { };
template <int F, int B>
struct is_foo_bar< foo_bar<F, B> > : std::true_type { };
static_assert(is_foo_bar< foo<1>::bar<2> >::value);
... it works. But this is not the way I want it to be. What I'm missing in code where bar
declaration is in foo
?
Using SFINAE it could be solved this way
template <class, class = void>
struct is_foo_bar : std::false_type { };
template <class T>
struct is_foo_bar<
T,
std::enable_if_t<
std::is_same_v< T, typename foo<T::f>::template bar<T::b> >
>
> : std::true_type { };
This works, but you need to specify all arguments explicitly, which may be a problem (for ex. variadic arguments).
EDIT: Found a soultion for variadic arguments
template <int F>
struct foo
{
template <int B, class... Params>
struct bar {
static constexpr int f = F;
static constexpr int b = B;
static constexpr size_t size = sizeof...(Params);
};
};
template <class, class = void>
struct is_foo_bar : std::false_type { };
template <template <int, class...> class T, int B, class... Params>
struct is_foo_bar<
T<B, Params...>,
std::enable_if_t<
std::is_same_v<
T<B, Params...>,
typename foo< T<B, Params...>::f >::template bar<B, Params...>
>
>
> : std::true_type { };
template <int, class...>
struct not_foo_bar {
static constexpr int f = 0;
};
static_assert(is_foo_bar< foo<1>::bar<2> >::value);
static_assert(is_foo_bar< foo<1>::bar<2, int> >::value);
static_assert(not is_foo_bar< not_foo_bar<1> >::value);
static_assert(not is_foo_bar< not_foo_bar<1, int> >::value);
This makes sure bar
belongs to foo
. When there is no such requirement it should be less ugly to just test for existence of needed members with std::void_t
and std::declval
...