What Are Some Uses of Template Template Parameters

What are some uses of template template parameters?

I think you need to use template template syntax to pass a parameter whose type is a template dependent on another template like this:

template <template<class> class H, class S>
void f(const H<S> &value) {
}

Here, H is a template, but I wanted this function to deal with all specializations of H.

NOTE: I've been programming c++ for many years and have only needed this once. I find that it is a rarely needed feature (of course handy when you need it!).

I've been trying to think of good examples, and to be honest, most of the time this isn't necessary, but let's contrive an example. Let's pretend that std::vector doesn't have a typedef value_type.

So how would you write a function which can create variables of the right type for the vectors elements? This would work.

template <template<class, class> class V, class T, class A>
void f(V<T, A> &v) {
// This can be "typename V<T, A>::value_type",
// but we are pretending we don't have it

T temp = v.back();
v.pop_back();
// Do some work on temp

std::cout << temp << std::endl;
}

NOTE: std::vector has two template parameters, type, and allocator, so we had to accept both of them. Fortunately, because of type deduction, we won't need to write out the exact type explicitly.

which you can use like this:

f<std::vector, int>(v); // v is of type std::vector<int> using any allocator

or better yet, we can just use:

f(v); // everything is deduced, f can deal with a vector of any type!

UPDATE: Even this contrived example, while illustrative, is no longer an amazing example due to c++11 introducing auto. Now the same function can be written as:

template <class Cont>
void f(Cont &v) {

auto temp = v.back();
v.pop_back();
// Do some work on temp

std::cout << temp << std::endl;
}

which is how I'd prefer to write this type of code.

Correct usage of C++ template template parameters

So I would like so make use of something like Bar<Foo<>>

template <typename T = int> 
struct Foo {
T t;
};

template <typename T>
struct Baz {
T t;
};

template <typename T>
struct Bar;

template <template <typename> class T, typename X>
struct Bar<T<X>> {
T<X> data;
X x;
};

int main()
{
Bar<Foo<>> a;
Bar<Baz<float>> b;
}

C++: Template Template Member of a Template Parameter as a Parameter to a Template Class Expecting a Template Template parameter

Starting with C++17, you do not need the template keyword between a :: and a member template specialization name in certain contexts that can only name a type, including the typename-specifier syntax, which is used in the LocalChecker alias template. So clang and MSVC are wrong to reject that line without the template, and possibly haven't yet implemented this change.

C++14 [temp.names]/4:

When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent
or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

was replaced with C++17 [temp.names]/4:

The keyword template is said to appear at the top level in a qualified-id if it appears outside of a template-argument-list or decltype-specifier. In a qualified-id of a declarator-id or in a qualified-id formed by a class-head-name or enum-head-name, the keyword template shall not appear at the top level. In a qualified-id used as the name in a typename-specifier, elaborated-type-specifier, using-declaration, or class-or-decltype, an optional keyword template appearing at the top level is ignored. In these contexts, a < token is always assumed to introduce a template-argument-list. In all other contexts, when naming a template specialization of a member of an unknown specialization ([temp.dep.type]), the member template name shall be prefixed by the keyword template.

Since the rules above only require the keyword template when a member template specialization is named, not just the member template itself, it would seem plain T::Checker, in addition to T::template Checker, ought to work at the part of the example using is_detected. (We don't want typename, since we're naming the alias template, not a type which is a specialization of that template.) But it's not clear why adding a template should make a difference in when a compiler does or doesn't need help determining the meaning. The open CWG issue 1478 is related.

In any case, the compilers do seem to like it better with the template hint: your program with is_detected<T::template Checker,... compiles successfully on clang++, g++, and msvc: see on godbolt.

What are some uses of template template parameters?

I think you need to use template template syntax to pass a parameter whose type is a template dependent on another template like this:

template <template<class> class H, class S>
void f(const H<S> &value) {
}

Here, H is a template, but I wanted this function to deal with all specializations of H.

NOTE: I've been programming c++ for many years and have only needed this once. I find that it is a rarely needed feature (of course handy when you need it!).

I've been trying to think of good examples, and to be honest, most of the time this isn't necessary, but let's contrive an example. Let's pretend that std::vector doesn't have a typedef value_type.

So how would you write a function which can create variables of the right type for the vectors elements? This would work.

template <template<class, class> class V, class T, class A>
void f(V<T, A> &v) {
// This can be "typename V<T, A>::value_type",
// but we are pretending we don't have it

T temp = v.back();
v.pop_back();
// Do some work on temp

std::cout << temp << std::endl;
}

NOTE: std::vector has two template parameters, type, and allocator, so we had to accept both of them. Fortunately, because of type deduction, we won't need to write out the exact type explicitly.

which you can use like this:

f<std::vector, int>(v); // v is of type std::vector<int> using any allocator

or better yet, we can just use:

f(v); // everything is deduced, f can deal with a vector of any type!

UPDATE: Even this contrived example, while illustrative, is no longer an amazing example due to c++11 introducing auto. Now the same function can be written as:

template <class Cont>
void f(Cont &v) {

auto temp = v.back();
v.pop_back();
// Do some work on temp

std::cout << temp << std::endl;
}

which is how I'd prefer to write this type of code.

Use template parameter of template template parameter

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.

Is it possible to deduce template template parameter parameter (no typo :-) )

It's certainly possible, the right syntax is simply:

template<typename T1, uint32_t TYP1, uint32_t STYP1,
typename T2, uint32_t TYP2, uint32_t STYP2>
void convert(TDataType<T1, TYP1, STYP1> from, TDataType<T2, TYP2, STYP2> &to)
{
to = static_cast<T2>(from.value()); // assuming TDataType has a ctor TDataType(T)
}

But it may be better to create a templated converting constructor that directly initializes from another TDataType instance:

template<typename T, uint32_t typeID, uint32_t subTypeID>
class TDataType {
T _value;
public:
TDataType(T value) : _value(value) {}

template<typename P, uint32_t pTypeID, uint32_t pSubTypeID>
TDataType(TDataType<P, pTypeID, pSubTypeID> that) : _value(static_cast<T>(that._value)) {}

template<typename, uint32_t, uint32_t>
friend class TDataType; // to be able to access that._value
};

Alternatively, use a user-defined conversion operator to make different TDataType's convertible to one another.

template<typename T, uint32_t typeID, uint32_t subTypeID>
class TDataType {
T _value;
public:
TDataType(T value) : _value(value) {}

template<typename P, uint32_t pTypeID, uint32_t pSubTypeID>
operator TDataType<P, pTypeID, pSubTypeID>() const {
return static_cast<P>(_value);
}
};

You can further specialize the conversion operator to do different things depending on the target type and/or declare the default specialization as deleted for a finer control of which conversions are allowed.

Then you'll be able to do:

TDataType<int32_t, 1, 32> Int{ 3 };
TDataType<double, 2, 2> Double = Int;

Using a type without template arguments as a template argument

You need to declare Container as a template template parameter. E.g.

template<typename Value, template <typename...> class Container, typename ID = size_t>
class Registry{

using Storage = Container<ID, Value>;
static_assert(std::is_same_v<Storage, std::map<ID, Value>> || std::is_same_v<Storage, std::unordered_map<ID, Value>>, "Underlying storage type must be a std::map-ish.");
public:
Storage store;
...

Other issues:

  • Storage is an instantiation, so don't specify template arguments for it.
  • Specify template arguments for std::map.
  • As the condition of static_assert you should use std::is_same_v (or std::is_same<...>::value instead.


Related Topics



Leave a reply



Submit