TorbenJ
TorbenJ

Reputation: 4582

Use template parameter of template template parameter

I'm currently playing around with templates in C++ and got stuck with template template parameters.

Lets say I have the following classes:

template<typename T>
struct MyInterface
{
    virtual T Foo() = 0;
}

class MyImpl : public MyInterface<int>
{
public:
    int Foo() { /*...*/ }
};

template< template<typename T> typename ImplType>
class MyHub
{
public:
    static T Foo()
    {
        ImplType i;
        return i.Foo();
    }

private:
    MyHub() { }
    ~MyHub() { }
};

In essence I would like to have a static class like MyHub that accepts an implementation of MyInterface and provides certain static methods to use them like static T Foo().

Then I tried to use MyHub:

int main()
{
    int i = MyHub<MyImpl>::Foo();

    return 0;
}

Unfortunately I always end up getting an error saying that the type T (of static T Foo() in MyHub) does not name a type.

I would expect that it works because

So far I couldn't find a solution for this after digging through documentations and google results so I hope some of you can help me.

Upvotes: 2

Views: 374

Answers (4)

KnightsWatch
KnightsWatch

Reputation: 114

The STL uses value_type as a place holder for the underlying type of a template class. You could possibly do the same for your solution.

template<typename T>
struct MyInterface
{
    typedef T value_type;
    virtual T Foo() = 0;
}

class MyImpl : public MyInterface<int>
{
public:
    int Foo() { /*...*/ }
};

template<typename ImplType>
class MyHub
{
public:
    static typename ImplType::value_type Foo()
    {
        ImplType i;
        return i.Foo();
    }

private:
    MyHub() { }
    ~MyHub() { }
};

Also note that in c++14, typename ImplType::value_type can be replaced by auto:

static auto Foo()
{
    ImplType i;
    return i.Foo();
}

Upvotes: 3

RamblingMad
RamblingMad

Reputation: 5518

MyImpl is not a class template; so can't be passed as the template parameter of MyInterface.

You could change your MyInterface, MyImpl and MyHub classes to:

template<typename T>
class MyInterface{
    public:
        virtual T foo() = 0;
};

class MyImpl: public MyInterface<int>{
    public:
        using value_type = int;

        value_type foo(){ return 1; /* dummy */ }
};

template<typename Impl, typename = std::enable_if_t<std::is_base_of<Impl, MyInterface<typename Impl::value_type>>::value>>
class MyHub{
    public:
        static auto foo(){
            static Impl i;
            return i.foo();
        }
};

Which lets you use it the same way you are in your example.

The std::is_base_of check might be a little unnecessary in this case; but, this way you can't accidentally pass in another class that isn't derived from MyInterface with a method foo().

Upvotes: 3

The names of template parameters of template template parameters are effectively a purely documentational construct—they don't get included in the containing template's scope.

There's good reason for that: there is nothing to whcih they could refer in the containing template. When you have a template template parameter, you must pass a template as the argument to it, and not an instantiation of a template. In other words, you're passing a template without arguments as the argument.

This means your code is simply wrong—you're using MyImpl as an argument for MyHub, but MyImpl is a class. MyHub expects a template, not a class. The correct instantiation of MyHub would be MyHub<MyInterface>. Not that there are no template arguments after this use of MyInterface; we are passing in the template itself, not an instantiation of it.

Template template parameters are used rather rarely in practice. You only use them if you want to instantiate the parameter template with your own types. So I would expect your MyHub code to do something like this:

template <template <class> class ImplTemplate>
struct MyHub
{
  typedef ImplTemplate<SomeMyHub_SpecificType> TheType;
  // ... use TheType
};

This doesn't seem to be what you want to do. I believe you want a normal type template parameter, and provide a nested typedef for its T. Like this:

template <class T>
struct MyInterface
{
  typedef T ParamType;  // Added

  virtual T Foo() = 0;
};


template<class ImplType>
class MyHub
{
    typedef typename ImplType::ParamType T;
public:
    static T Foo()
    {
        ImplType i;
        return i.Foo();
    }

private:
    MyHub() { }
    ~MyHub() { }
};

int main()
{
    int i = MyHub<MyImpl>::Foo();

    return 0;
}

Upvotes: 2

a_pradhan
a_pradhan

Reputation: 3295

You can use typedefs. Also, since your implementation classes are not template class, there is no need for template template parameters.

#include <iostream>
#include <string>

template<typename T>
struct MyInterface
{
    virtual T Foo() = 0;
    typedef T Type;
};

class MyIntImpl : public MyInterface<int>
{
public:
    int Foo() { return 2; }
};

class MyStringImpl : public MyInterface<std::string>
{
public:
    std::string Foo() { return "haha"; }
};

template<class ImplType>
class MyHub
{
public:
    static typename ImplType::Type Foo()
    {
        ImplType i;
        return i.Foo();
    }

private:
    MyHub() { }
    ~MyHub() { }
};

int main()
{
    std::cout << MyHub<MyIntImpl>::Foo() << "\n"; // prints 2
    std::cout << MyHub<MyStringImpl>::Foo() << "\n"; // print haha
    return 0;
}

Here is an example.

Upvotes: 5

Related Questions