Type trait that returns the template parameter of a template function

202 views Asked by At

Suppose one has the following template function:

template<typename T>  auto foobar ()  { return std::string("why not"); }

Question: is it possible to define a type trait that provides the template parameter of the function ?

If we call get_template_parameter such a trait, one could then write:

template<typename T>  auto foobar ()  { return std::string("why not"); }

using type = typename get_template_parameter <decltype(foobar<double>)>::type;

static_assert (std::is_same_v<type,double>);

UPDATE: I added a dummy result to the foobar function in order to underline the fact that the template parameter is not related to the result of the function.

3

There are 3 answers

6
David G On

Yes, all you need is:

template<typename T> T foobar();

using type = decltype(foobar<double>());

static_assert(std::is_same_v<type, double>);
0
0xbachmann On

If you are willing write your functions as functors you can extract the template type as follows:

template<typename T>
struct foobar {
    auto operator()() const { return std::string("why not"); }
};

template<typename T>
struct get_template_parameter;

// specialization for functor with single template parameter
template<template<typename> class Functor, typename T>
struct get_template_parameter<Functor<T>>
{
    using type = T;
};

int main() {
    static_assert(std::is_same_v<double, get_template_parameter<foobar<double>>::type>);
}

2
0xbachmann On

An other option, less intrusive than making the function a functor or returning a pair, would be to add the templated type T as the last argument of the function with a default argument, allowing the function to be called with the same signature than before.

Then the type can be determined, by looking at decltype(foobar<double>) and extracting the last argument as presented here.

template<typename T>
struct tag
{
    using type = T;
};

template<typename... Ts>
struct select_last
{
    // Use a fold-expression to fold the comma operator over the parameter pack.
    using type = typename decltype((tag<Ts>{}, ...))::type;
};

template<typename T>
struct get_template_parameter;

template<typename R, typename... T>
struct get_template_parameter<R(T...)> : select_last<T...> {
};

template<typename T>
using get_template_parameter_t = typename get_template_parameter<T>::type;

template <typename T>
auto foobar([[maybe_unused]] T t = {}) {
    return std::string();
}


int main() {
    static_assert(std::is_same_v<double, get_template_parameter_t<decltype(foobar<double>)>>);
}

Note that this can be problematic, if T is somehow not easy to construct. Then an option would be to make the last argument a reference of T and letting the tag remove the reference again:

template<typename T>
struct tag
{
    using type = std::remove_reference_t<T>;
};

/* code as before ... */

template <typename T>
auto func([[maybe_unused]] T& t = {}) {
    return std::string();
}