In C++, is there any way to ensure that two or more template type parameters are themselves template types with a common template type parameter?
Let’s say that I have this:
struct ArbitraryType {};
template <typename T>
class ArbitraryTemplateClass1 {
/* ... */
};
template <typename T>
class ArbitraryTemplateClass2 {
/* ... */
};
And I want a template that can take ArbitraryTemplateClass1
and ArbitraryTemplateClass2
as template type parameters, but ensure they both have a common template type parameter T
(and then do something with the type T
). For example, I’d like to be able to do something like:
template <typename U <typename T>, typename V<T>> // desired (but incorrect) syntax
struct CommonTemplateTypeParameterProducer {
T Produce() { return T(); }
};
// Which would allow this:
int main() {
CommonTemplateTypeParameterProducer<ArbitraryTemplateClass1<ArbitraryType>,
ArbitraryTemplateClass2<ArbitraryType>> producer;
// Type of t should be ArbitraryType
auto t = producer.Produce();
// This should fail to instantiate CommonTemplateTypeParameterProducer because U and V don't share a common T
//CommonTemplateTypeParameterProducer<ArbitraryTemplateClass1<ArbitraryType>,
// ArbitraryTemplateClass2<int>> producer2;
}
Are there any mechanisms that allow this kind of behavior without having direct knowledge of the arbitrary template classes and the arbitrary type? (something like adding a typedef to ArbitraryTemplateClass1
that specifies the type of T
would not be an option). If possible, I would also like to avoid adding a third template type parameter to CommonTemplateTypeParameterProducer;
ideally I would like the template to be able to deduce the common type that it is enforcing.
Note: Something like this would also help with being able to ensure that two template type parameters that are also variadic templates both have identical template arguments.
I have looked into C++ concepts/constraints/requirements and template template parameters, but so far none seem to offer solutions to this particular problem.
>Solution :
It’s possible, but might not be the best design, because if somebody will need to add another template parameter to ArbitraryTemplateClass1
, everything will break. Or if you decide to make a version of it that’s not templated, and only works for one specific type.
It’s better to add something like using type = T;
to ArbitraryTemplateClass1
/2
, and check that in your template instead of the actual template parameter. CommonTemplateTypeParameterProducer
should have no business policing the template arguments of its template arguments.
#include <concepts>
template <typename T>
struct A
{
using type = T;
};
template <typename T>
struct B
{
using type = T;
};
template <typename X, typename Y>
requires std::same_as<typename X::type, typename Y::type>
struct Foo
{
X x;
Y y;
};
int main()
{
Foo<A<int>, B<int>> foo;
}
Here’s another option. It has the same issue as your proposed design, but at least you don’t need to spell the template argument twice:
template <typename T>
struct A {};
template <typename T>
struct B {};
template <
template <typename> typename X,
template <typename> typename Y,
typename T
>
struct Foo
{
X<T> x;
Y<T> y;
};
int main()
{
Foo<A, B, int> foo;
}
And lastly, here’s exactly what you asked for. I added a helper template to extract the template argument from arbitrary templates. This, again, has the issue I described above, and also requires spelling the template argument twice.
#include <concepts>
template <typename T>
struct GetTemplateArgument {};
template <template <typename> typename T, typename U>
struct GetTemplateArgument<T<U>> {using type = U;};
template <typename T>
struct A {};
template <typename T>
struct B {};
template <typename X, typename Y>
requires std::same_as<typename GetTemplateArgument<X>::type, typename GetTemplateArgument<Y>::type>
struct Foo
{
X x;
Y y;
};
int main()
{
Foo<A<int>, B<int>> foo;
}