I've some trouble to make use of template template parameters. Here is a very simplified example:
template <typename T>
struct Foo {
T t;
};
template <template <class X> class T>
struct Bar {
T<X> data;
X x;
};
int main()
{
Bar<Foo<int>> a;
}
The compiler (g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2) reports the following error:
main.cpp:8:5: error: ‘X’ was not declared in this scope
T<X> data;
^
main.cpp:8:6: error: template argument 1 is invalid
T<X> data;
^
Any idea what's wrong?
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;
}
template <typename T>
struct Foo {
T t;
};
template <template <class> class T, class X>
struct Bar {
T<X> data;
X x;
};
int main()
{
Bar<Foo, int> a;
}
In
template <template <class X> class T>
The template type parameter X is not a template parameter to the outermost template: it is a template parameter to the innermost template. It's rather similar to
int foo(int (*bar)(int x))
{
int y = x; // compiler error
}
which doesn't work since the function takes a single argument, bar: there is no argument x.
Depending upon what you are truly trying to do, you could add the second template parameter, with something like
template <typename X, template <typename> class T >
struct Bar
{
// ...
};
you can keep the declaration with a single type parameter, but pattern match to give a partial specialization that would define the class in the example context
template <typename T>
struct Bar;
template <typename X, template <typename> class T >
struct Bar<T<X>>
{
// ...
};
you could modify Foo to have a useful nested type, and grab it that way
template <typename T>
struct Bar
{
using X = T::value_type;
};
or you could define a metafunction that extracts a template parameter from a template type, and get it that way:
template <typename T>
struct Bar
{
using X = get_parameter<T>;
};
The most flexible is the last version, except rather than a metafunction that extracts template arguments, you would declare a get_bar_parameter function, and define a partial specialization that extracts the template parameter from a Foo<X> (or a T<X>). That way, if you ever decide in the future to use Bar with classes where the right value of X isn't computed that way, you can do so by giving an appropriate specialization for get_bar_parameter.
// method 1
template <typename T>
struct Foo {
typedef T Type;
T t;
};
template <typename T>
struct Bar {
T data;
typename T::Type x;
};
// method 2
template <typename T>
struct Hack
{
T t;
};
template <typename T>
struct TypeOf
{
typedef struct UnknownType Type;
};
template<>
struct TypeOf< Hack<int> >
{
typedef int Type;
};
template <typename T>
struct Baz {
T data;
typename TypeOf<T>::Type X;
};
int main()
{
Bar< Foo<int> > a;
Baz< Hack<int> > b;
return 0;
}
In method 1 information is provided with nested type. This requires changing original class.
In method 2 this information is provided with specialization of another template.
Related
Suppose I had a templated object in C++ Test<T> and I wanted to find out what the T value is so that when I pass Test<T> as a template argument to TestWrapper<Test<T>>, I can declare a variable called T extradata in the object TestWrapper that is the same as Test's T type.
How can I achieve this by only modifying TestWrapper (i.e. making no changes to Test struct) ? Additionally is it possible to modify it so that it will extract from any object that takes 1 template parameter e.g. foo<T>, bar<T>, rather then just Test<T>>?
Thanks in advance.
template<typename T>
struct Test {
T value;
};
//template -> extract first type parameter from any object as T
struct TestWrapper {
T extradata;
};
int main() {
TestWrapper<Test<int>> temp;
temp.extradata = 123; // temp.extradata is a int, deduced from Test<int>
}
You can use partial template specialization to do that.
template<typename T>
struct Test {
T value;
};
template <typename T>
struct TestWrapper;
template <template <typename> typename Outer, typename T>
struct TestWrapper<Outer<T>> {
T extradata;
};
int main() {
TestWrapper<Test<int>> temp;
temp.extradata = 123; // temp.extradata is a int, deduced from Test<int>
}
In this example we have not defined any default definition for TestWrapper, so it will fail to compile if you give something that does not match the specialization.
You can
// primary template
template <typename T>
struct TestWrapper {
T extradata; // declare extradata with type T
// how to declare it depends on your intent
};
// partial specialization
template <template <typename...> class C, typename T, typename... Args>
struct TestWrapper<C<T, Args...>> {
T extradata; // T is the 1st template parameter of C here
};
You can create a helper class to extract the first parameter of the instantiated template, for example:
template<class>
struct Extract;
template<template<class...> class V, class First, class... Rest>
struct Extract<V<First, Rest...>> {
using type = First;
};
template<class T>
struct TestWrapper {
typename Extract<T>::type extradata;
};
Demo.
Trying to use a template defined in a class that is being passed as a template parameter itself. The typename specifier is obviously incorrect but i just get other errors if i try to use template there.
struct Tee
{
template<typename T = int> class Bar { };
};
template<typename T>
struct Foo
{
typename T::Bar<> f; // *error non-template used as template
};
int main() {
Foo<Tee> foo;
return 0;
}
I have a question about templates and it is in the code:
template<typename T>
struct foo {
T t;
};
template<typename FooType>
struct bar {
T t; //<- how to get T here (preferably without using typedef in foo)
};
Here's a generic template argument type extractor:
#include <tuple>
template <typename> struct tuplify;
template <template <typename...> class Tpl, typename ...Args>
struct tuplify<Tpl<Args...>>
{
using type = std::tuple<Args...>;
};
template <typename T, unsigned int N>
using get_template_argument
= typename std::tuple_element<N, typename tuplify<T>::type>::type;
Usage:
get_template_argument<std::vector<int>, 1> a; // is a std::allocator<int>
Or in your case:
get_template_argument<FooType, 0> t;
If I understood your question correctly, you could use template specialization as follows. Given your foo<> class template:
template<typename T>
struct foo {
T t;
};
Define a bar<> primary template and a corresponding specialization this way:
template<typename FooType>
struct bar;
template<typename T>
struct bar<foo<T>> {
T t; // T will be int if the template argument is foo<int>
};
Under the assumption that you are always supposed to instantiate bar by providing an instance of foo<> as the type argument, you can leave the primary template undefined.
The specialization will match the foo<T> pattern, thus giving you the type with which foo<> is instantiated in T.
Here is how you could test the validity of this approach with a simple program:
#include <type_traits>
int main()
{
bar<foo<int>> b;
// This will not fire, proving T was correctly deduced to be int
static_assert(std::is_same<decltype(b.t), int>::value, "!");
}
Here is the corresponding live example.
If you don't want or can't add a typedef to foo, you can additionally write an independent "extractor" template
template <typename T> struct ExtractT;
template <typename T> struct ExtractT<foo<T> > {
typedef T type;
};
and use it as
template<typename FooType>
struct bar {
typename ExtractT<FooType>::type t;
};
You can take that ExtractT one step further and decouple it from foo
template <typename T> struct ExtractT;
template <template <typename> class C, typename T> struct ExtractT<C<T> > {
typedef T type;
};
and so on until you reinvent something from Boost or C++11 standard library :) BTW, this feels like something that should already be available in form of a more generic solution....
template <template <typename> class F> struct call_me {};
template <typename T> struct maybe;
template <typename... T> struct more;
int main()
{
call_me<maybe> a; // ok
call_me<more> b; // error
}
I understand why call_me<more> fails. But I want to make it work.
Is there a workaround that doesn't involve changing call_me (or add an specialization to it)?
template <template <typename> class F> struct call_me {};
template <typename T> struct maybe;
template <typename... T> struct more;
template <template <class...> class F> struct just_one {
template <class A> using tmpl = F<A>;
};
int main()
{
call_me<maybe> a;
call_me<just_one<more>::tmpl> b;
}
Not exactly equivalent, but maybe close enough.
template <typename T> using onemore = more<T>;
int main()
{
call_me<onemore> b;
}
You could wrap more:
template <template <typename...> class Tmpl>
struct variwrap
{
template <typename> struct Dummy
{
template <typename ...Brgs>
struct rebind
{
typedef Tmpl<Brgs...> other;
};
};
};
Now you can say call_me<variwrap<more>::Dummy>, and the consumer can use F::rebind<Args...>::other to recover more<Args...>. Of course call_me has no way of knowing that F has the rebind member, so you'll need to add a specialization.
Yuck.
My question is w.r.t the following thread : specialize a member template without specializing its parent
I'm absolutely fine with the standard saying that it is illegal to do so. But i want to understand why is it illegal to do so? What would be impact had it been allowed?
Maybe because of something like this:
template <typename T>
struct foo
{
template <typename U>
struct bar
{
typedef U type;
};
};
template <typename T>
struct foo<T>::bar<int> // imaginary
{
typedef void type;
};
template <>
struct foo<float>
{
template <typename U>
struct bar
{
typedef U* type;
};
};
// is it void [foo<T>::bar<int>] or
// int* [foo<float>::bar<U>]?
typedef foo<float>::bar<int>::type ambiguous;
A sensible solution is to say "we'll make the entire thing explicit".