Gwangmu Lee
Gwangmu Lee

Reputation: 487

Un-specializing C++ template parameter

Basically what I want to do is as follows. Let's assume we have a template member function foo:

template <typename T>
T SomeClass::foo();

and somehow a user passed map< string, int > as a template argument:

foo<map<string, int>>();

What I want to do here is, when defining the function foo, to get the inner types, string and int. I tried much guesswork to un-specialize the argument but was of no avail.

template <map<typename K, typename V>>
map<K, V> SomeClass::foo();  // absolutely illegal

I thought about using partial specialization, but it didn't work as foo is a class member function.

Upvotes: 0

Views: 654

Answers (3)

Jeffrey Harper
Jeffrey Harper

Reputation: 888

Here's another possible solution. The method foo dispatches to a foo_detail method that take a pointer to T as a parameter. That parameter is not used in foo_detail. Instead, the parameter allows the overload resolution to select which foo_detail is called.

The solution has a clunky feel because of the unused parameter. Fortunately, this can be hidden in the private portion of SomeClass so that users of SomeClass don't have to know about it.

#include <map>
#include <iostream>
#include <string>
#include <typeinfo>
using std::map;
using std::cout;
using std::endl;
using std::string;

class SomeClass
{
public:
    template <typename T>
    T foo()
        {
            return foo_detail((T *)0);
        }

private:    
    template<typename T>
    T foo_detail(T *)
        {
            cout << "foo called with type " << typeid(T).name() << endl;
            return T();
        }

    template <typename K, typename V>
    map<K, V> foo_detail(map<K, V> *)
        {
            cout << "foo map specialization called with types "
                 << typeid(K).name() << ' ' << typeid(V).name() << endl;
            return map<K,V>();
        }
};

int main()
{
    SomeClass s;
    s.foo<double>();
    s.foo<map<int, string> >();
    return 0;
}

Upvotes: 0

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145259

Off the cuff:

template< class T >
struct Foo
{
    static auto impl() -> T;
};

template< class K, class V >
struct Foo< map< K, V > >
{
    static auto impl() -> map< K, V >;
};

template< class T >
auto foo()
    -> T
{ return Foo<T>::impl(); }

Upvotes: 1

Vittorio Romeo
Vittorio Romeo

Reputation: 93274

If you want a generic way of grabbing inner types from a template, you can use explicit specialization:

template <typename T>
struct unpack;

template <template <typename...> class C, typename A, typename B>
struct unpack<C<A, B>>
{
    using first  = A;
    using second = B;
};

Usage:

static_assert(std::is_same_v<string,
    typename unpack<map<string, int>>::first
>);

static_assert(std::is_same_v<int,
    typename unpack<map<string, int>>::second
>);

If you only care about doing that when calling a function, you can just make the function a template:

template <typename K, typename V>
void foo(std::map<K, V>);

Upvotes: 1

Related Questions