Passing the specified template type as a template parameter

Say I have a template type ...

template <typename T> struct Foo { Foo(T t) {} }; 

Is there a way to pass the specified type of Foo to a function so that the function has direct visibility T?

Ideally, I could write something like this ...

 Foo<int> foo = create<Foo<int>>(); 

The closest I could come with 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 will then be used as

 Foo<int> foo = create<Foo, int>(); 

Thanks for any help.

+5
source share
4 answers

This form of the 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 should replace typename with a class pointer:

 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 are compiled and equivalent.


To your syntax Foo<int> foo = create<Foo<int>>(); worked, you just need to do this:

 template <typename T> T create() { return T{}; } 

If you want to limit which type can be sent, you must create a type character:

 // 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 tag:

 template < typename T, void_t<first_param_t<T>>* = nullptr > // ^---- if the typedef is not defined, it a subtitution error. T create() { return T(first_param_t<T>{}); } 

You can implement void_t as follows:

 template<typename...> using void_t = void; 

Live at coliru

+5
source

Why not just use a tag manager, for example:

 template <class> struct tag { }; template <class T> Foo<T> create(tag<Foo<T>>) { return Foo<T>(T()); } //... Foo<int> foo = create(tag<Foo<int>>{}); 
+4
source

One easy way is to directly add subtype information to Foo :

 template <typename T> struct Foo { using type = T; Foo(T t) {} }; 

and then

 template <typename FooT> FooT create() { return FooT(typename FooT::type{}); } 

You can add SFINAE if you want:

 template <typename FooT> auto create() -> decltype(FooT(typename FooT::type{})) { return FooT(typename FooT::type{}); } 

If you really want to restrict the function to only Foo , you need to create traits and SFINAE on it.

+3
source

In C ++ 11

Demo

The bottom line is to have an entry point function called create that can create a create_helper structure to create the correct type.

We can create our structures using specialized specialization, so we force the template template to be passed.

Full code:

 template<class T> struct create_helper { static_assert(sizeof(T) == 0, "Need to pass templated type to create"); }; template <class T, template<class> class TT> struct create_helper<TT<T>> { static TT<T> apply() { return {T{}}; } }; template<class T> auto create() -> decltype(create_helper<T>::apply()) { return create_helper<T>::apply(); } 

And the test:

 template<class T> struct Foo { Foo(T t){std::cout << "Constructed Foo with value " << t << std::endl;} }; int main() { Foo<int> foo = create<Foo<int>>(); } 

Output:

 Constructed Foo with value 0 
+2
source

Source: https://habr.com/ru/post/1268784/


All Articles