Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing specified template type as template parameter

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.

like image 413
Daskie Avatar asked Jun 12 '17 18:06

Daskie


People also ask

Can a template be a template parameter?

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.)

What is meant by the template parameter it can be used to pass a type as an argument it can be used to evaluate a type it can of no return type it can be used to delete a type?

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.

How will you restrict the template for a specific datatype?

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.

What is template type parameter?

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.


3 Answers

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

like image 179
Guillaume Racicot Avatar answered Sep 19 '22 08:09

Guillaume Racicot


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.

like image 34
Jarod42 Avatar answered Sep 18 '22 08:09

Jarod42


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>>{});
like image 37
W.F. Avatar answered Sep 21 '22 08:09

W.F.