Is there a way to extract a partial default specialization from the compiler?
Say that I have this two parameter template:
template<typename A, typename B>
struct X {
A a;
B b;
};
and I also have some code that makes use of a single parameter template, like this:
template<template<typename> class T, typename B>
struct make_T_of_B {
T<B> member;
};
I'd like to be able to say:
make_T_of_B<X<int>, double> dummy;
where X<int> is taken as a single parameter template. It would be equivalent to this template:
template<typename B>
struct Y {
int a;
B b;
};
which looks like how one would specialize X<int, B> without actually changing anything. It's in a way similar to a default specialization -- except that a default specialization doesn't produce another template but rather an actual type (in other words, it's always total).
I realize that I can cascade the template arguments
template<typename A>
struct Z1 {
// start from scratch
template<typename B>
struct Z2 {
A a;
B b;
};
// inherit from double template above
template<typename B>
struct X: ::X<A, B> {};
};
make_T_of_B<Z1<int>::Z2, double> dummy1;
make_T_of_B<Z1<int>::X, double> dummy2;
but I find that to be rather hard to read and not communicate my intentions clearly.
Thank you.
I misunderstood your question. All you want is a way to bind the first template parameter, which you can do easily like this:
template <typename T> using Foo = X<int, T>;
Now Foo<double> is the same as X<int, double>.
Without C++11-style aliases, you can achieve the same with a bit more boilerplate:
template <typename T> struct Foo
{
typedef X<int, T> type;
};
Now you use Foo<double>::type.
I'd use a trait:
template <typename> struct applicator;
template <template <typename> class Tmpl, typename T>
struct applicator<Tmpl<T>>
{
template <typename A>
using rebind = make_T_of_B<Tmpl, A>;
};
Now you can say:
applicator<X<int>>::rebind<double> dummy;
You can of course also move the second argument, A, into the main template:
template <typename, typename> bpplicator;
template <template <typename> class Tmpl, typename T, typename A>
struct bpplicator<Tmpl<T>, A>
{
using type = make_T_of_B<Tmpl, A>; // or "typedef make_T_of_B<Tmpl, A> type;"
};
bpplicator<X<int>, double>::type dummy;
This has the advantage that it works in C++03, too.
Related
I'm trying to partially specialize a template for a metafunction and ran into a problem.
I specialized the template like this:
template <typename A, typename B>
struct Foo;
template <typename A, typename B1>
struct Foo<A, typename A::template Bar<B1>> {
/* use both A and B1*/
};
template <typename A, typename B1>
struct Foo<A, typename A::template Xyz<B1>> {
/* use both A and B1*/
};
However this results (Visual Studio 2019) in
Error C2764: 'B1': template parameter not used or deducible in partial specialization 'Foo<A,A::Bar<B1>>' (5, 47)
I assume this is because I used the template parameter A as a qualifier in the specialication (typename A::template Bar<B1>).
Is there any way to circumvent this and use parameters in template specializations as qualifiers?
Note: In my usecase the first parameter is never really specialized.
Theoretically it could work to nest the specialized template in another template class (i.e. currying the metafunction), but templates can only be specialized at namespace scope.
Using a template template parameter may work out:
template <typename A, typename B>
struct Foo;
template <typename TA, template<class> class TBar, typename B1>
struct Foo<TA, TBar<B1>> {};
Given
struct A
{
template<class T>
struct Bar {};
};
you can form
Foo<A, A::Bar<int>> x;
and it will deduce A, A::Bar and int in the specialization for you. But note that no attempt is made to check that the A in A::Bar matches the A given as first template parameter; it's unclear what you'd expect to happen for, say, a Foo<double, A::Bar<int>>.
https://godbolt.org/z/hGhsZm
I assume this is because I used the template parameter A as a qualifier in the specialication (typename A::template Bar).
I don't think so.
Suppose A is as follows
struct A
{
template <typename B>
using Bar = int;
};
and that you define a Foo<A,A::Bar<B1>>.
But A::Bar<B1> is int !
So you're defining Foo<A, int>.
How can, the compiler, deduce B1 from int ?
It seems to me that it can't.
Possible solution (depending from your needs): if you need to specialize through B1, but you need A::Bar<B1> inside Foo, you can use B1 itself as second parameter and A::Bar<B1> as using type inside Foo
template <typename A, typename B1>
struct Foo<A, B1> {
using bType = A::template Bar<B1>;
};
Is it possible to have a struct which may or may not have a member? Something like this:
template <typename T, typename A = some_type_with_size_0>
struct s {
T t;
A aux;
};
To be specific, if I asked for s<int, int> I would get a struct with two ints, but if I asked for s<int> I would get a struct with only an int.
In C++20, it will be possible to do what you're trying to do directly:
template <typename T, typename A = some_type_with_size_0>
struct s {
T t;
[[no_unique_address]] A aux;
};
See https://en.cppreference.com/w/cpp/language/attributes/no_unique_address.
In C++17, there's no straightforward way to specify a member that conditionally disappears. You need to write a full-blown partial specialization, like so:
template <typename T, typename A = void>
struct s {
T t;
A aux;
};
template <typename T>
struct s<T, void> {
T t;
};
This unfortunately requires you to repeat yourself in typing out all the common members (in this case only t). To avoid this, we can stick the conditionally present members in a base class:
template <typename T, typename A = void>
struct s : optional_aux<A> {
T t;
};
template <typename A>
struct optional_aux {
A aux;
};
template <>
struct optional_aux<void> { };
In the case where A = void, this base class is empty, so the compiler has discretion to remove it entirely, making sizeof(s<T, void>) potentially equal to sizeof(T). The [[no_unique_address]] attribute basically makes empty base class optimization available for members as well.
You can use a variadic template:
template <typename...> struct Generic;
template <typename T1> struct Generic<T1> {
T1 field1;
};
template <typename T1, typename T2> struct Generic<T1, T2> {
T1 field1;
T2 field2;
};
Given the following class:
template <class T, template <typename> class B>
class A { B<T> b; };
I can now write code like such:
A<float, MyVector> a1;
A<int, MySet> a2;
What is the most elegant way to put multi-parameter classes of which all parameters are specified except one, in B? Like a map with int-keys? The only thing I can come up with is this:
template <class U> using C = MyMap<int, U>;
A<float, C<int>> a3;
Is there such a template equivalent to std::bind, where we can provide only a part of the parameters and leave one of them open? I'm quite sure the language doesn't provide for this, but people must've solved this before.
A<float, MyMap<int, _>> a3;
There isn't a built-in template equivalent to std::bind, but you can write one yourself. Here's a simple version which binds the first template argument which you could extend to suit your needs:
template <typename T, template <typename...> class B>
struct bind_t1 {
template <typename... Ts>
using type = B<T,Ts...>;
};
Then you just use bind_t1 like so:
A<float, bind_t1<int, std::map>::type> a3;
Note that for your example, you'll need to modify your template parameters to take a variadic template template:
template <class T, template <typename...> class B>
class A { B<T> b; };
Here's a slightly extended version which can bind a number of contiguous elements at the start of the parameter list:
template <template <typename...> class B, typename... Ts>
struct bind_nt1 {
template <typename... Us>
using type = B<Ts...,Us...>;
};
//Usage
A<std::less<int>, bind_nt1<std::map, int, float>::type> a3;
Here's a generic version based on the way std::bind does things. It doesn't do any validation and probably has some edge cases, but it's a good starting point. Thanks to Piotr Skotnicki for improvements.
template <std::size_t N>
struct placeholder{};
template <template <typename...> class B, typename... Ts>
struct bind_t {
private:
template <typename T, typename UTuple>
struct resolve_placeholder {
using type = T;
};
template <std::size_t N, typename UTuple>
struct resolve_placeholder<placeholder<N>, UTuple> {
using type = typename std::tuple_element<N-1, UTuple>::type;
};
public:
template <typename... Us>
using type = B<typename resolve_placeholder<Ts, std::tuple<Us...>>::type...>;
};
//Usage
A<int, bind_t<std::map, float, placeholder<1>, std::less<float>>::type> a3;
Using this, you can even change the order of template parameters:
//std::map<int,float>
bind_t<std::map, placeholder<2>, placeholder<1>>::type<float, int> b;
Having a class like the A, is there a way to apply it to a template like this of B, with T2 set to some type C? But without creating another template class inheriting from A.
template<typename T1, typename T2>
class A
{ };
template<template <typename T1> class T3>
class B
{ };
With C++11 using a template alias works:
template<typename T1, typename T2>
class A
{ };
template<template <typename T1> class T3>
class B
{ };
class C
{ };
template< typename T > using A1 = A< T, C >;
int main()
{
B< A1 > b;
}
without C++11, you are left with what you probably already know:
template< typename T > class A1 : A< T, C > {};
I will propose an alternative solution: do not use template template parameters.
If you write:
template <typename T> struct B {};
Then it can be used with A<int, int> or C<3> or even plain D.
Whilst it is possible to use template template parameters, it is general a bad idea. You should treat the template parameter of a class as an implementation detail and apply the golden rule: do not rely on implementation details.
If you need access to the type, somehow, then use an associated type (aka T::AssociatedType) or a trait (BTraits<T>::AssociatedType).
EDIT: dealing with multiple instantiations of the template template parameter.
Suppose we want to "erase" the template template parameter of such a class:
template <template <typename> class A>
struct Something {
template <typename T>
void doit() { A<T>::doit(); }
};
The C++ standard allocation model is to use an inner rebind structure:
template <typename T>
struct Simple {
template <typename U>
struct rebind { typedef Simple<U> type; };
};
template <typename T0, typename T1>
struct Multi {
template <typename U>
struct rebind { typedef Multi<U, T1> type; };
};
template <typename A>
struct Something {
template <typename T>
void doit() { typedef typename A::rebind<T>::type B; B::doit(); }
};
Note how you can use complex computations in rebind and nothing forces you in blindly passing the type received as parameter.
Whilst another (similar) solution is to ask for a factory (aka, the object passed itself cannot be used but it can build useful objects); for ease of use the C++ containers ask of their allocators that they be both usable in themselves and factories for other types.
Yes, you can do it using C++11's alias template:
template <typename T>
using AA = A<T, C>;
B<AA> b;
Live example
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....