In the snippet below, base_type_t
has 2 template arguments; T1
and k1
. The templates foo_t
, bar_t
, and baz_t
derive from this base, using the derived template's parameter for T1
, but providing a specific value for k1
.
In a function template, I want the TemplatedType
argument to accept only such subclasses of base_type_t
, which do already have the argument k1
specified, but the argument T1
unspecified. Does C++ allow this?
template<typename T1, int k1>
struct base_type_t {};
template<typename T1>
struct foo_t : public base_type_t<T1, 1> {};
template<typename T1>
struct bar_t : public base_type_t<T1, 1> {};
template<typename T1>
struct baz_t : public base_type_t<T1, 16> {};
// How do I restrict `TemplatedType` to be a child of `base_type_t`,
// which has the parameter `k1` already specified?
template<template<typename> typename TemplatedType>
void UseModel()
{
// Only the template argument `T1` of `base_type_t` is specified.
// The argument `k1` must be specified by `TemplatedType`.
TemplatedType<float> instance1 { };
TemplatedType<bool> instance2 { };
}
int main()
{
UseModel<foo_t>();
UseModel<bar_t>();
UseModel<baz_t>();
return 0;
}
Maybe you can declare a couple of function with the same name. The first one, a template one and more specific, receiving a base_type_t
; the second one generic receiving a generic value
template <int k>
std::true_type checkBase (base_type_t<float, k> const &);
std::false_type checkBase (...);
Then a little using
to check if a generic type is (or derive from) a base_type_t
type
template <typename T>
using IsBaseTypeChild = decltype(checkBase(std::declval<T>()));
Now you can use SFINAE to enable UseModel()
only if TemplatedType<int>
can call the specialized version of checkBase()
.
template<template<typename> typename TemplatedType>
std::enable_if_t<IsBaseTypeChild<TemplatedType<float>>::value> UseModel()
{
[[maybe_unused]] TemplatedType<float> instance1 { };
[[maybe_unused]] TemplatedType<bool> instance2 { };
}
The following is a full compiling example. Observe that I've added a call to a bad_t
type that can't call UseModel()
#include <type_traits>
template<typename T1, int k1>
struct base_type_t {};
template<typename T1>
struct foo_t : public base_type_t<T1, 1> {};
template<typename T1>
struct bar_t : public base_type_t<T1, 1> {};
template<typename T1>
struct baz_t : public base_type_t<T1, 16> {};
template<typename T1>
struct bad_t {};
template <int k>
std::true_type checkBase (base_type_t<float, k> const &);
std::false_type checkBase (...);
template <typename T>
using IsBaseTypeChild = decltype(checkBase(std::declval<T>()));
template<template<typename> typename TemplatedType>
std::enable_if_t<IsBaseTypeChild<TemplatedType<float>>::value> UseModel()
{
[[maybe_unused]] TemplatedType<float> instance1 { };
[[maybe_unused]] TemplatedType<bool> instance2 { };
}
int main()
{
UseModel<foo_t>();
UseModel<bar_t>();
UseModel<baz_t>();
// UseModel<bad_t>(); // <--- compilation error if you enable this line:
// bad_t doesn't derive from base_type_t
return 0;
}