Say I have some template type...
template <typename T> struct Foo {
Foo(T t) {}
};
Is there a way to pass a specified Foo type to a function so that the function has direct visibility of T?
Ideally I would be able to write something like this...
Foo<int> foo = create<Foo<int>>();
The closest I've been able to come is
template <
template <typename> typename TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
which would then be used like
Foo<int> foo = create<Foo, int>();
Thanks for any help.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
A template parameter is a specific form of the parameter that can be used to pass a type as an argument. These parameters can be used by these function templates just like any other ordinary type.
There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
This form of template template parameter is only allowed in C++17:
template < // v---------- typename here not allowed
template <typename> typename TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
You must replace the typename
pointed out by class
:
template < // v---------- class allowed
template <typename> class TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
In C++17, both compiles and are equivalent.
To make your syntax Foo<int> foo = create<Foo<int>>();
work, you simply need to do this:
template <typename T>
T create() {
return T{};
}
If you want to limit what type can be sent, you must create a type trait:
// default case has no typedef
template<typename>
struct first_param {};
// when a template is sent, define the typedef `type` to be equal to T
template<template<typename> class TT, typename T>
struct first_param<TT<T>> {
using type = T;
};
// template alias to omit `typename` everywhere we want to use the trait.
template<typename T>
using first_param_t = typename first_param<T>::type;
Then, use your trait:
template <
typename T,
void_t<first_param_t<T>>* = nullptr
> // ^---- if the typedef is not defined, it's a subtitution error.
T create() {
return T(first_param_t<T>{});
}
You can implement void_t
like this:
template<typename...>
using void_t = void;
Live at Coliru
One simple way is to add the sub-type information in Foo
directly:
template <typename T> struct Foo {
using type = T;
Foo(T t) {}
};
and then
template <typename FooT>
FooT create() {
return FooT(typename FooT::type{});
}
You might add SFINAE if you want:
template <typename FooT>
auto create()
-> decltype(FooT(typename FooT::type{}))
{
return FooT(typename FooT::type{});
}
If you want really restrict the function to Foo
exclusively, you have to create a traits and SFINAE on it.
Why not simply use a tag dispatching, e.g.:
template <class>
struct tag { };
template <class T>
Foo<T> create(tag<Foo<T>>) {
return Foo<T>(T());
}
//...
Foo<int> foo = create(tag<Foo<int>>{});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With