I have a template class inside another template class. How can I write a template function that will accept any combination of inner/outer template class?
template <class X>
struct A
{
template <class Y>
struct B
{
int q;
};
};
template <class X, class Y>
int f( typename A<X>::template B<Y>& ab )
{
return ab.q;
}
int g( A<char>::B<short>& ab )
{
return f( ab ); // Error: Could not deduce template argument
}
This template accepts any combination of inner and outer:
tempalte <typename T>
int f(T& ab)
{
return ab.q;
}
You cannot deduce X and Y from typename A<X>::template B<Y>. See here for details: What is a nondeduced context?. The fundamental issue is that there is no 1:1 relation between the actual type that the name typename A<X>::template B<Y> refers to and X and Y. Consider that there could be a specialization:
template <>
struct A<double>
{
template <class Y>
using B = A<int>::B<Y>;
};
Now A<double>::B<Y> and A<int>::B<Y> refer to the same type. Given one or the other there is no way to unambiguously deduce X.
The compiler is not allowed to infer the template arguments in this context, so in order to transport the type information, you have to make it explicit.
One possibility is to provide aliases:
template <class X>
struct A
{
template <class Y>
struct B
{
using X_type = X;
using Y_type = Y;
int q;
};
};
template <class T>
int f( T& ab )
{
return ab.q;
}
int g( A<char>::B<short>& ab )
{
return f( ab );
}
This way, you still have access to the types as e.g. typename T::X_type.
Related
I have a class with multiple template parameters; let us say it looks something like this:
template <class T, class B>
struct Vector2 : B
{
Vector2() noexcept;
constexpr explicit Vector2(T a) noexcept;
}
Template parameter B always depends on T. For example if T is float B will be XMFLOAT2, if T is int B will be XMINT2. For this I created a template specialization:
template class Vector2<float, XMFLOAT2>;
template class Vector2<int32_t, XMINT2>;
template class Vector2<uint32_t, XMUINT2>;
The problem is that since B always depends on T, I want to call Vector<float> for example, and the expression should expand to Vector<float, XMFLOAT2>.
I thought of doing a typealias, however, I wouldn't be sure how to accomplish this, since it would need to be specialized again.
template<class T> using Vector2 = Vector2<T, ??>;
That doesn't really make sense...
How can I call a class with multiple template parameters using just a single parameter with the others being deduced? Or is there a different approach?
You can create helper trait:
template <typename T>
struct vector_base_class;
template <> struct vector_base_class<float> { using type = XMFLOAT2; };
template <> struct vector_base_class<int32_t> { using type = XMINT2; };
template <> struct vector_base_class<uint32_t> { using type = XMUINT2; };
template <class T>
struct Vector2 : typename vector_base_class<T>::type
{
Vector2() noexcept;
constexpr explicit Vector2(T a) noexcept;
};
As mentioned in a comment, you are probably looking for a trait:
template <typename T>
struct xm_from_T;
template <class T, class B = typename xm_from_T<T>::type>
struct Vector2 : B
{
Vector2() noexcept;
constexpr explicit Vector2(T a) noexcept;
};
Then specialize the trait:
template <> struct xm_from_T<float> { using type = XMFLOAT2; };
template <> struct xm_from_T<int32_t> { using type = XMINT2; };
template <> struct xm_from_T<uint32_t> { using type = XMUINT2; };
Say I have some template type...
template <typename T> struct Foo {
Foo(T t) {}
};
Is there a way to pass a specified Foo type to a function so that the function has direct visibility of T?
Ideally I would be able to write something like this...
Foo<int> foo = create<Foo<int>>();
The closest I've been able to come is
template <
template <typename> typename TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
which would then be used like
Foo<int> foo = create<Foo, int>();
Thanks for any help.
This form of template template parameter is only allowed in C++17:
template < // v---------- typename here not allowed
template <typename> typename TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
You must replace the typename pointed out by class:
template < // v---------- class allowed
template <typename> class TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
In C++17, both compiles and are equivalent.
To make your syntax Foo<int> foo = create<Foo<int>>(); work, you simply need to do this:
template <typename T>
T create() {
return T{};
}
If you want to limit what type can be sent, you must create a type trait:
// default case has no typedef
template<typename>
struct first_param {};
// when a template is sent, define the typedef `type` to be equal to T
template<template<typename> class TT, typename T>
struct first_param<TT<T>> {
using type = T;
};
// template alias to omit `typename` everywhere we want to use the trait.
template<typename T>
using first_param_t = typename first_param<T>::type;
Then, use your trait:
template <
typename T,
void_t<first_param_t<T>>* = nullptr
> // ^---- if the typedef is not defined, it's a subtitution error.
T create() {
return T(first_param_t<T>{});
}
You can implement void_t like this:
template<typename...>
using void_t = void;
Live at Coliru
One simple way is to add the sub-type information in Foo directly:
template <typename T> struct Foo {
using type = T;
Foo(T t) {}
};
and then
template <typename FooT>
FooT create() {
return FooT(typename FooT::type{});
}
You might add SFINAE if you want:
template <typename FooT>
auto create()
-> decltype(FooT(typename FooT::type{}))
{
return FooT(typename FooT::type{});
}
If you want really restrict the function to Foo exclusively, you have to create a traits and SFINAE on it.
Why not simply use a tag dispatching, e.g.:
template <class>
struct tag { };
template <class T>
Foo<T> create(tag<Foo<T>>) {
return Foo<T>(T());
}
//...
Foo<int> foo = create(tag<Foo<int>>{});
In C++11
Demo
The gist is to have an entry point function named create that can instantiate a create_helper struct to create the proper type.
We can create our structures using template specialization so that we're forcing a templated class to be passed.
Full code:
template<class T>
struct create_helper
{
static_assert(sizeof(T) == 0, "Need to pass templated type to create");
};
template <class T, template<class> class TT>
struct create_helper<TT<T>>
{
static TT<T> apply()
{
return {T{}};
}
};
template<class T>
auto create() -> decltype(create_helper<T>::apply())
{
return create_helper<T>::apply();
}
And a test:
template<class T>
struct Foo
{
Foo(T t){std::cout << "Constructed Foo with value " << t << std::endl;}
};
int main()
{
Foo<int> foo = create<Foo<int>>();
}
Output:
Constructed Foo with value 0
(this question is not related to C++11/C++14: the examples are compiled using C++03)
enable_bool<T> has a member ::type only when T is bool
template <class T>
struct enable_bool
{};
template <>
struct enable_bool< bool >
{ typedef bool type; };
In the next snippet, the partial specialization is correct (see gcc.godbolt.org)
template <class T, class U, class Enable = T>
struct Foo
{
static int bar() { return 0; }
};
template <class T, class U>
struct Foo< T, U, typename enable_bool<T>::type >
{
static int bar() { return 1; }
};
int main()
{
return Foo <int, bool>::bar();
}
As enable_bool<T>::type already corresponds to T (when T is bool)
we are tempted to factorize parameters T and Enable.
But compiler complains (see gcc.godbolt.org)
template <class T, class U>
struct Foo
{
static int bar() { return 0; }
};
template <class T, class U> //ERROR non-deducible template parameter 'T'
struct Foo< typename enable_bool<T>::type, U >
{
static int bar() { return 1; }
};
Why compiler cannot deduce template parameter T in this above partial specialization?
Finally the question was not even related to SFINAE!
Consider this very simple snippet without SFINAE:
enable<T>::type is always same as T whatever the provided T type.
template <class T>
struct enable
{ typedef T type; }; // Enable always
template <class U, class V>
struct Foo
{
static int bar() { return 0; }
};
template <class X, class Y> //ERROR non-deducible parameter 'X'
struct Foo< typename enable<X>::type, Y >
{
static int bar() { return 1; }
};
int main()
{
return Foo<int, bool>::bar();
}
When compiler tries to match Foo<int, bool> with Foo<typename enable<X>::type,Y>
1st param U = int <--> enable<X>::type => Cannot deduce X
2nd param V = bool <--> Y
Compiler is not designed to deduce X from equation int = enable<X>::type.
Therefore, compiler needs some help from developer.
Another parameter is required: Enable.
The below fixed snippet add the Enable class template parameter.
The compiler performs the following matching:
1st param U =int <--> X
2nd param V =bool<--> Y
3rd param Enable=int <--> enable<X>::type (deduced X from 1st param)
(3rd param is int because declaration class Enable=U means by default 3rd param same as 1st one)
Fixed snippet:
template <class T>
struct enable
{ typedef T type; }; // Enable always
template <class U, class V, class Enable = U>
struct Foo
{
static int bar() { return 0; }
};
template <class X, class Y> // Compiler can deduce 'X'
struct Foo< X, Y, typename enable<X>::type >
{
static int bar() { return 1; }
};
int main()
{
return Foo<int, bool>::bar();
}
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.
I want to specialize a class template with the following function:
template <typename T>
class Foo
{
public:
static int bar();
};
The function has no arguments and shall return a result based on the type of Foo. (In this toy example, we return the number of bytes of the type, but in the actual application we want to return some meta-data object.)
The specialization works for fully specified types:
// specialization 1: works
template <>
int Foo<int>::bar() { return 4; }
// specialization 2: works
template <>
int Foo<double>::bar() { return 8; }
// specialization 3: works
typedef pair<int, int> IntPair;
template <>
int Foo<IntPair>::bar() { return 2 * Foo<int>::bar(); }
However, I would like to generalize this to types that depend on (other) template parameters themselves.
Adding the following specialization gives a compile-time error (VS2005):
// specialization 4: ERROR!
template <>
template <typename U, typename V>
int Foo<std::pair<U, V> >::bar() { return Foo<U>::bar() + Foo<V>::bar(); }
I am assuming this is not legal C++, but why? And is there a way to implement this type of pattern elegantly?
Partitial specialization is valid only for classes, not functions.
Workaround:
template <typename U, typename V>
class Foo<std::pair<U, V> > {
public:
static int bar() { return Foo<U>::bar() + Foo<V>::bar(); }
};
If you does not want to specialize class fully, use auxiliary struct
template<class T>
struct aux {
static int bar();
};
template <>int aux <int>::bar() { return 4; }
template <>int aux <double>::bar() { return 8; }
template <typename U, typename V>
struct aux <std::pair<U, V> > {
static int bar() { return Foo<U>::bar() + Foo<V>::bar(); }
};
template<class T>
class Foo : aux<T> {
// ...
};
It is perfectly legal in C++, it's Partial Template Specialization.
Remove the template <> and if it doesn't already exists add the explicit class template specialization and it should compile on VS2005 (but not in VC6)
// explicit class template specialization
template <typename U, typename V>
class Foo<std::pair<U, V> >
{
public:
static int bar();
};
template <typename U, typename V>
int Foo<std::pair<U, V> >::bar() { return Foo<U>::bar() + Foo<V>::bar(); }