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;
}