I have a class Foo and class Bar. They are wrappers over std::array. Both of them have some derived classes.
template<typename T, std::size_t N>
struct Foo {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct FooDerived : Foo <T, N> {};
template<typename T, std::size_t N>
struct Bar {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct BarDerived : Bar <T, N> {};
And I want to implement tuple-interface in std namespace: get / tuple_size / tuple_element. But these methods should be available only for Foo and derived from Foo classes.
namespace std {
template<template<typename, std::size_t> class T, typename TArg, std::size_t NArg>
class tuple_size<T<TArg, NArg>>
: public integral_constant<std::size_t, NArg>
{
};
template<std::size_t I, template<typename, std::size_t> class T, typename TArg, std::size_t NArg>
struct tuple_element<I, T<TArg, NArg>>
{
using type = TArg;
};
} // namespace std
That works, but works also with Bar and Bar-derived classes.
I thought to use std::enable_if with std::is_base_of. std::enable_if for classes can be used as a template parameters. And if I write:
template<template<typename, std::size_t> class T, typename TArg, std::size_t NArg,
typename std::enable_if<std::is_base_of<Foo<TArg, NArg>, T<TArg, NArg>>::value, int>::type = 0>
class tuple_size<T<TArg, NArg>>
: public integral_constant<std::size_t, NArg>
{
};
it leads to compile error: default template arguments may not be used in partial specializations.
Is it possible to forbid to use tuple-like interface for non-related to Foo classes?
Example: http://rextester.com/JEXJ27486
Update: Seems I find out the solution. More flexible than just static_assert. If it is impossible to use default template arguments in partial specialization - add additional class with full template specialization.
template<typename T, typename TArg, std::size_t NArg, typename = void>
struct tuple_resolver;
template<typename T, typename TArg, std::size_t NArg>
struct tuple_resolver<T, TArg, NArg,
typename std::enable_if<std::is_base_of<Foo<TArg, NArg>, T>::value>::type>
: public integral_constant<std::size_t, NArg>
{
using type = TArg;
};
template<template<typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
class tuple_size<T<TArg, NArg>>
: public tuple_resolver<T<TArg, NArg>, TArg, NArg>
{
};
Example: http://rextester.com/KTDXNJ90374
typename std::enable_if<...>::type = 0
is even supposed to mean as a template parameter declaration. What happens if you use the usualtypename = std::enable_if<...>::type
syntax? – Daniel Scheplertypename = std::enable_if<true, int>::type
because, as error says: default template arguments may not be used in partial specializations. – Vladislavtypename std::enable_if<cond, int>::type = 0
which does make sense, even if it's a bit roundabout. – Daniel Scheplerstd::enable_if_t<c, int> = 0
is less typing and supports overloading on mutually exclusive conditions, whereastemplate <typename = std::enable_if_t<c>> void foo(); template <typename = std::enable_if_t<!c>> void foo();
is ill-formed, two declarations of the same template with different defaults. – Oktalist