Template Metaprogramming: (Trait For) Dissecting a Specified Template into Types T<T2,T3 N,T4, ...>

template metaprogramming: (trait for?) dissecting a specified template into types TT2,T3 N,T4, ...

Here's an idea:

 template <typename T, typename ...>
struct tmpl_rebind
{
typedef T type;
};

template <template <typename ...> class Tmpl, typename ...T, typename ...Args>
struct tmpl_rebind<Tmpl<T...>, Args...>
{
typedef Tmpl<Args...> type;
};

Usage:

typedef std::vector<int> IV;
typedef typename tmpl_rebind<IV, std::pair<double, std::string>>::type PV;

Now PV = std::vector<std::pair<double, std::string>>.

Finding a type (maybe underlying) from a structure type for the usage in templates

You have two options to reduce the number of arguments:

  1. Option 1. Pass array template argument and extract the element type with ARRAY::value_type
  2. Option 2. If the container is always going to be an array of a certain size, pass the element type and construct array type from it.

How to put different template types into one vector

Option 1 : make sure that all different types of arguments derive from a base class and use pointers to that class. Note that this option is risky in terms of memory management. You might want to make it safer by using boost::shared_ptr instead of pointers. Otherwise, you must manually clean up when an item is being removed from the vector.

Option 2 (my personal favorite) : use Boost.Variant to make a typedef of all possible argument types and use that typedef as the argument type in std::vector

typedef boost::variant<ArgumentType1, ArgumentType2, ArgumentType3> ArgumentType;
std::vector<ArgumentType> vec;

Template for different data types with similar properties

The class could looks like the following, with a member function to print the matrix:

template<typename T, std::size_t N, std::size_t M = N>
class Matrix
{
public:
Matrix()
{
// handle filling here
}
void print() const
{
for(int i = 0 ; i < N;i++) {
for(int j = 0; j < M;j++) {
std::cout << data[i][j] << " ";
}
std::cout << std::endl;
}

}

private:
T data[N][M];
};

Or also have a function as follow:

template<typename T, std::size_T N, std::size_T M = N>
void print(const Matrix<T,N,M>& mat)
{
// print ...
}

How to instantiate a class using an explicit first, and a default second argument, via the meta model?

with class model it is ...

Class<Example,[]|[String, String=]> exampleModel = `Example`;
value e1 = exampleModel();
value e2 = exampleModel("foo");
value e3 = exampleModel("foo", "bar");

or with class declaration it is ...

ClassDeclaration exampleDeclaration = `class Example`;
assert(is Example e1 = exampleDeclaration.instantiate());
assert(is Example e2 = exampleDeclaration.instantiate([], "foo"));
assert(is Example e3 = exampleDeclaration.instantiate([], "foo", "bar"));

HTH

C++ class template is a template: template argument is invalid

#include <complex>
#include <vector>
template<typename T>
class Fred {
std::vector<T> data_;
};

int main(){
//Fred<std::vector<double> > works;
Fred<std::vector<std::complex<double> > > doesnt_work;
return 0;
}

Works well. You miss third > in declaration of doesnt_work.

Abstracting over types of non-type template parameters

The answer might come a little late...

Missed Try...

(see Correct Answer & C++17 Solution below)



This original answer is kept as a souvenir of my very first answer on SO.

Not exactly a fail, one would say. But rather, a first missed try... ;)

Now, jump to the next horizontal line...

I was searching for an answer to a related question when I came across this question. After having read it, I told myself: "Humm... That's something I already did. And it worked. How did I do it exactly?!". And then, I continued searching for the answer to my question...

Today I felt like I should take a little time to propose a solution to this problem (two actually).

As you already noticed, the problem comes from the fact that the compiler has no clue on how to deduce T. One can interpret the error message as "Please, give me a little help on that T".

The first version I did that was working had the specialization of foo deriving from a class similar to std::integral_constant. Making foo derive from std::integral_constant<T, x> might have had help the compiler to figure out the type of T. (or maybe MSVC -vs2019- had been a little nice with me)

Anyway, meanwhile I found better solution. And there should have no way for the compiler not to be able to deduce the type of T, because, there is no need of a typename T parameter for the type of x...


Here it is: (C++17 Solution)

template<typename T> struct foo {};

template<auto x, template<decltype(x)> class X>
struct foo<X<x>> {
using arg_type = decltype(x);
static constexpr arg_type n = x;
};

//template<int x> struct bar { enum { n = x }; };
template<int x> struct bar;

using bar_16 = foo<bar<16>>;
using bar_16_arg_t = typename bar_16::arg_type; // int
constexpr auto bar_16_n = bar_16::n; // 16

Note that for this to work, it is not even needed for bar to be a complete type. A forward declaration (like in this example) is perfectly enough for the decomposition to work.

Enjoy...



Correct Answer

° Notes

  • This answers the question asked 9 years ago, and,
  • The solution proposed here uses only C++11 features.
  • This solution manages only integral types.

    (other type is let as an exercise for the reader)



    If your compiler supports C++17 features, the solution posted above should be preferred, as it manages not only integral types.

Only interested in a working code example?

jump to: "Working Solutions"

° Preamble

  • After my researches, it seems that a solution to this particular problem hasn't been found (or not published) until now. I thought I should go in a little more details about the "Whys" and "Hows". I hope it will be appreciated, but overall: useful...
  • I am currently in the process of writing a meta-programming library full of compile-time tools and features. I hope to be able to release it on GitHub anytime soon.

    (who knows) – Anyway...
  • What hasn't been my frustration when I realized that my first answer was correct only since C++17... – Not exactly "on time", one could say...:P
  • With all the machinery of "compile-time only" features I have in mind ATM, I was feeling like there should have had a means to do that 9 years ago.
  • I begun to think about how I would have had done that using only C++11 features and, after an hour or so, I found a working solution. (two in fact)
  • It took me a little more time to make it a usable solution. (two in fact)
  • And quite a bit more to right this post... :D



    After all, there might have compilers

    "just good enough" to understand C++11 only... :P

Obviously, as the set of features available at that time was narrower,
the solution found is "just a bit" more verbose... :D

° Searching Process

Firstly, one has to keep in mind that when a compiler outputs "cannot deduce"...

– It does not mean that there is an error (though there might have one).

– It rather means that the compiler is not as clever as one thinks it is.

– It means that one has to give a hand to the compiler for it to be able to do its job...

In clear?

– The compiler is kindly asking for you to do a part of its job.

– And there's good chances for you:

  • To end up doing most of the job yourself... :P

Here, the compiler says "cannot deduce the type of T".

Indeed, T is not used in the expression used as argument of the specialization of foo, and thus, it cannot be deduced from there...

One first has to do something to signify the relation between typename T and the value of x (which is of type T). What instantly comes to mind is that one needs a template similar to that of std::integral_constant which does exactly that. It encodes, into a new type, a value along with its corresponding type.

Disclaimer [! Warning! ]

  • People susceptible to develop allergic reactions at sight of uppercase letters within identifier names should not continue reading this post!

Nothing new until there?

Perfect! Here it is:

template<typename T, T V>
struct NonTypeParam { using Type = T; static constexpr T Value = V; };

Next one needs something to create instances of the NonTypeParam template with a value and its corresponding type...

  • It might be a template taking a type parameter.
  • This parameter would receive the type to decompose.
  • Then, one would have to specialize it somehow...

Let's give it a try, and start with:

template<typename T> struct Extract { using Result = void; };

To fully abstract the specialization of the Extract template, one would have to write something like:

template<typename T, T V, template<T> class C>
struct Extract<C<V>> { using Result = NonTypeParam<T, V>; };

Which leads to the same problem, because it is the same type of specialization used in the question. At that point, one has to remind what the compiler was unable to do. It "cannot deduce" what type the parameter T is supposed to alias in our specialization...

In fact, the message is misleading somehow, because T is not even part of the expression passed as argument of the specialization. Thus, the problem is not to attribute a typename to the parameter T, it is rather to attribute a type to the parameter V...

Now, one should be able to ask the right questions:

  1. How can T be removed from the equation?
    • By explicitly defining the type of V.
  2. What are the possible types for the value of V?
    • The types allowed as non-type template parameters.

First things first, how would look a specialization by explicitly defining the type of V for char, for example? It would look like that:

template<char V, template<char> class C>
struct Extract<C<V>> { using Result = NonTypeParam<char, V>; };

, that's a little bit annoying, but as there is a finite number of possibilities. One might find a way to reduce the declarations later on. Let's add another specialization, a victim template, and put that to the test...

template<typename T, T V>
struct NonTypeParam { using Type = T; static constexpr T Value = V; };

template<typename T> struct Extract { using Result = void; };

template<char V, template<char> class C>
struct Extract<C<V>> { using Result = NonTypeParam<char, V>; };

template<std::size_t V, template<std::size_t> class C>
struct Extract<C<V>> { using Result = NonTypeParam<std::size_t, V>; };

template<std::size_t I> struct TestNonType1 {};

using Result = typename Extract<TestNonType1<42>>::Result;
using RType = typename Result::Type; // std::size_t
constexpr auto rValue = Result::Value; // 42

No surprises, it works as expected...

... What are the possible types now?

According to the standard on template parameters:

A non-type template parameter must have a structural type, which is one of the following types (optionally cv-qualified, the qualifiers are ignored):

  • lvalue reference type (to object or to function);
  • an integral type;
  • a pointer type (to object or to function);
  • a pointer to member type (to member object or to member function);
  • an enumeration type;
  • std::nullptr_t; (since C++11)

For our case, the question asked for integral types.

Well, what does the standard says about integral types.

Let's have a look at std::is_integral to find out:

..., if T is the type bool, char, char8_t (since C++20), char16_t, char32_t, wchar_t, short, int, long, long long, or any implementation-defined extended integer types, including any signed, unsigned, and cv-qualified variants.

Ouch!

As there is 9 types – if one exclude char8_t (only from C++20) and consider that implementation-defined integer types are most of the time aliases of these integral type – one would have to do specializations for:

  • 9 signed.
  • 9 signed const.
  • 9 signed volatile.
  • 9 signed const volatile.
  • Which does 36 specializations.
  • And then, add 36 more for the unsigned versions?!

Disclaimer [ Notice ]

  • Without a doubt, this is the reason why no one (maybe really no one else) did that before...


Wait, wait, wait a minute...

One should think about that a second time and, once again, ask the right questions:

  • How is a non-type parameter 'read'/'interpreted' ?
  • Does it make any sense for it to be volatile ?
  • If its value is part of the typename, isn't const implied somehow ?

You certainly found the answers by yourself...

– Likewise, there is no unsigned version of char16_t, char32_t and wchar_t.

– Moreover, if one reads a second time a little more carefully what the standard says about template parameters, one might see something that haven't had the attention it deserves...

A non-type template parameter must have a structural type, which is one of the following types (optionally cv-qualified, the qualifiers are ignored)

Well, well, well...

– This will do a lot more job than one would have excepted at first... :P

– It turn out that, in the end, only 14 specializations of the Extract template are enough to manage 99% of all possible integral types...

... I think that's way too much writings for such a small amount of code.

Please find the solutions here below, – let here for the posterity – in hope it might be useful to someone (at least for the interesting 'trickery' used in the second example).

° Personal Comment

It is hard to me to believe that this 9 years old question haven't find an answer earlier (as well as thinking that I would be the only "dumb" guy who found this answer)



Working Solutions

Solution #1

Nothing special here. It's just a regular specialization of a template...

template<typename T, T V>
struct NonTypeParam { using Type = T; static constexpr T Value = V; };

namespace Details1 {

template<typename T> struct Extract { using Result = void; };

template<typename T, T V> using R = NonTypeParam<T, V>;

// boolean
template<bool V, template<bool> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
// signed types
template<char V, template<char> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<char16_t V, template<char16_t> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<char32_t V, template<char32_t> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<wchar_t V, template<wchar_t> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<short V, template<short> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<int V, template<int> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<long V, template<long> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<long long V, template<long long> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
// unsigned types
template<unsigned char V, template<unsigned char> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<unsigned short V, template<unsigned short> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<unsigned int V, template<unsigned int> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<unsigned long V, template<unsigned long> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<unsigned long long V, template<unsigned long long> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };

} /* namespace Details1 */

template<typename T>
struct Extract1
{
using Result = typename Details1::Extract<T>::Result;
};

// Victim template:
template<std::size_t I> struct TestNonType1 {};

// Usage:
using Param = typename Extract1<TestNonType1<42>>::Result;
using PType = typename Param::Type; // std::size_t
constexpr auto pValue = Param::Value; // 42

Solution #2

In this solution, one leverages the power of decltype to declare function template overloads, which will never be defined anywhere...

template<typename T, T V>
struct NonTypeParam { using Type = T; static constexpr T Value = V; };

namespace Details2 {

template<typename T, T V> using R = NonTypeParam<T, V>;

// boolean
template<bool V, template<bool> class C> R<decltype(V), V> Extract(C<V> && _);
// signed types
template<char V, template<char> class C> R<decltype(V), V> Extract(C<V> && _);
template<char16_t V, template<char16_t> class C> R<decltype(V), V> Extract(C<V> && _);
template<char32_t V, template<char32_t> class C> R<decltype(V), V> Extract(C<V> && _);
template<wchar_t V, template<wchar_t> class C> R<decltype(V), V> Extract(C<V> && _);
template<short V, template<short> class C> R<decltype(V), V> Extract(C<V> && _);
template<int V, template<int> class C> R<decltype(V), V> Extract(C<V> && _);
template<long V, template<long> class C> R<decltype(V), V> Extract(C<V> && _);
template<long long V, template<long long> class C> R<decltype(V), V> Extract(C<V> && _);
// unsigned types
template<unsigned char V, template<unsigned char> class C> R<decltype(V), V> Extract(C<V> && _);
template<unsigned short V, template<unsigned short> class C> R<decltype(V), V> Extract(C<V> && _);
template<unsigned int V, template<unsigned int> class C> R<decltype(V), V> Extract(C<V> && _);
template<unsigned long V, template<unsigned long> class C> R<decltype(V), V> Extract(C<V> && _);
template<unsigned long long V, template<unsigned long long> class C> R<decltype(V), V> Extract(C<V> && _);

} /* namespace Details2 */

template<typename T>
struct Extract2
{
using Result = decltype(Details2::Extract(std::declval<T>()));
};

// Victim template:
template<unsigned long long I> struct TestNonType2 {};

// Usage:
using Param = typename Extract2<TestNonType2<42>>::Result;
using PType = typename Param::Type; // std::size_t
constexpr auto pValue = Param::Value; // 42

° Update (July 25, 2021)

  • Here below is an example of how a template declared with any type of non-type parameter can be decomposed.
  • Unfortunately, though this little piece of code seems to be using only C++11 language features, it cannot be compiled as C++11.
  • This code works perfectly, and does what it is meant to do, but is has to be compiled as C++17.
  • There definitely had a change in the standard since the addition of auto as a non-type template parameter which, I think (but couldn't find info on it), make the compilers interpret the pattern <typename T, template <T> class C, T V> as if it was a synonym of <auto V>.
/* Template allowing to separately retrieve the components
* of a template having one non-type parameter.
*/
template<typename T, template <T> class C, T V>
struct TmplInfo;

/* Function to decompose a template having one non-type
* parameter and return its corresponding TmplInfo type.
*/
template<typename T, template <T> class C, T V>
inline constexpr TmplInfo<T, C, V> ToTmplInfo(C<V> && o);

/* Our victim template...
*/
template<std::size_t I> struct Victim;

/* Aliases Victim<42> and then decompose it to a TmplInfo.
*/
using V42 = Victim<42>;
using VInfo = decltype(ToTmplInfo(std::declval<V42>()));

/* Compiled for x64 arch, this gives:
* using VInfo = TmplInfo<std::size_t, Victim, 42Ui64>;
*/

Is it possible to define an alias for a template-template parameter?

You cannot make an alias for T. The following was discussed in the committee to make an alias for T (because a very late C++11 draft contained notes that stated that it is an alias for T which a Defect Report cleaned up).

// Courtesy of @KerrekSB
template <template <typename> class T, typename R> class Unit
{
template <typename U> using MyTemplate = T<U>;
// ...

// use e.g. MyTemplate<int> to get T<int>
};

Note while MyTemplate<int> is the same type as T<int>, that MyTemplate is not the same as T. The wording at http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1286 was supposed to change that, but in the last meeting it was considered to be a very special machinery that doesn't really fit what alias templates turned out to be (own templates), and it was pushed back to review. To get that effect, a using MyTemplate = T; in future may fit the bill (if proposed and accepted, of course).

std::common_type trait for user defined types

According to the draft standard N4582 §20.13.2 Header synopsis [meta.type.synop] (Emphasis Mine):

The behavior of a program that adds specializations for any of the
templates defined in this subclause is undefined unless otherwise
specified
.

Consequently, adding specializations for type_traits can cause undefined behaviour unless somewhere else in the standard there's a wording for a specific type trait that supersedes the wording displayed above. Fortunately, in Table 60 - Other transformations:

Sample Image

There's the wording:

A program may specialize this trait if at least one template parameter
in the specialization is a user-defined type. [ Note: Such
specializations are needed when only explicit conversions are desired
among the template arguments. — end note ]

This means that specializations of std::common_type type trait that have at least one user-defined type are perfectly allowed. In fact if you take a look at §20.15.4.3 Specializations of common_type [time.traits.specializations] you'll find out that STL already defines specializations of std::common_type for user defined types std::chrono::duration and std::chrono::time_point.

Thus, the correct way to make common_type "work" for user defined types is to provide a specialization of it for those specific types, e.g.,

struct A {};
struct B {};

namespace std {
template<>
struct common_type<A, B> {
using type = A;
};
}

In the code example above we specify that the common type between A and B is A.



Related Topics



Leave a reply



Submit