In my quest to better understand templates and meta-programming in C++ I'm reading this article, but my understanding of the code snippets quickly diminishes, e.g.:
template<class A, template<class...> class B> struct mp_rename_impl;
template<template<class...> class A, class... T, template<class...> class B>
struct mp_rename_impl<A<T...>, B>
{
using type = B<T...>;
};
template<class A, template<class...> class B>
using mp_rename = typename mp_rename_impl<A, B>::type;
The code is used like:
mp_rename<std::pair<int, float>, std::tuple> // -> std::tuple<int, float>
mp_rename<mp_list<int, float>, std::pair> // -> std::pair<int, float>
mp_rename<std::shared_ptr<int>, std::unique_ptr> // -> std::unique_ptr<int>
Can someone please explain the code like I'm five?
I do have a general and basic understanding of non-templated C++.
What I don't get is:
Why is mp_rename_impl forward declared with two type parameters (class A, template<class...> class B), then it's defined and specialized at the same time[*] with three (template<class...> class A, class... T, template<class...> class B) and respectively two(A<T...>, B) type parameters?
I understand that it aliases (using type = B<T...>;) the type to be B<T...> instead of A<T...>, but I don't really get how it is done.
Also why is A a template template parameter only in the specialization?
[*] most certainly I got something wrong here
Why is mp_rename_impl forward declared with two type parameters (class A, template<class...> class B), then it's defined and specialized at the same time[*] with three (template<class...> class A, class... T, template<class...> class B) and respectively two(A<T...>, B) type parameters?
The forward declaration establishes the number of parameters needed to instantiate mp_rename_impl, and that the former should be an actual type, the latter a template.
Then when there's an actual instantiation, it tries to match the specialisation struct mp_rename_impl<A<T...>, B>, and in doing so it can consider any combination of values for the specialisation's A, T... and B matching the specialisation's expectations: namely template<class...> class A, class... T, template<class...> class B. Note that the A parameter in the specialisation shares a name with the A in the declaration, but is not the same - the former being a template and the latter a type. Effectively, to match the specialisation a template instantiation must have been passed as the declaration's A parameter, and that template's parameters are captured at T.... It imposes no new restrictions on what can be passed as B (though the using statement does - B<T...> needs to be valid or you'll get a compilation error - too late for SFINAE to kick in).
Also why is A a template template parameter only in the specialization?
The specialisation calls that parameter A, but it's not conceptually the same as A in the declaration. Rather, the former's A<T...> corresponds to the latter A. Perhaps the specialisation should have called it "TA" or something else to indicate it's the template from which an actual A can be formed in combination with the T... parameters.
The specialisation is then of A<T...>, B, so the compiler works backwards from whatever instantiation is actually attempted to find valid substitutions for A, T... and B, guided by the restrictions on their form specified in template<template<class...> class A, class... T, template<class...> class B>.
What this achieves it to make sure the specialisation is only matched when the two parameters are a template already given some set of parameters types, and a template able to take a list of parameter types. The matching process effectively isolates the T type list so it can be reused with B.
My first attempt wasn’t what you were looking for, so let me briefly try to go back and explain like you’re six.
It’s not forward-declared in the sense that a function has a prototype and a definition. There’s an implementation for any A, and that compiles to an empty struct (which is a unique type to the compiler, but doesn’t require any actual storage or run-time code). Then, there’s a second implementation, only for template classes A.
There are really two templates in the second definition. What’s going on is that the second definition it takes the two parameters A and ... T and turns them into a type A<T>, which becomes the first parameter of mp_rename_impl<A<T...>,B>. So it applies to any A that’s a template class. But that’s a more specific kind of A! So it’s a specialization that needs to declare a struct with a type definition in its scope. Finally, the third variant is not a specialization of the template at all. It’s declaring the templated mp_rename as an alias for the more complicated type stored within the scope of every struct in the second declaration, which as you see is the identifier type in the scope mp_rename_impl<A, B>. Believe it or not, this makes his template code a lot more readable.
Why does the more generic definition at the top expand to just an empty struct? When A is not a template class, the contents are trivial, but it does need some kind of type to its name so the compiler will consider it distinct from every other type. (It would have been more cool to write my example below to generate classes with the static constants as members, rather than functions. In fact, I just did.)
Updated as threatened to make my templates a bit more like his:
Okay, template metaprogramming is a kind of programming where, instead of having the program compute something when it runs, the compiler computes it ahead of time and stores the answer in the program. It does this by compiling templates. This can be a lot faster to run, sometimes! But there are limits on what you can do. Mainly, you can’t modify any of your parameters, and you’ve got to be sure the computation stops.
If you’re thinking, “You mean, just like functional programming?” you are one very smart five-year-old. What you normally end up doing is writing recursive templates with base cases that expand either to unrolled, streamlined code, or to constants. Here’s an example that might seem familiar from your Intro to Computer Science class when you were three or maybe four:
#include <iostream>
using std::cout;
using std::endl;
/* The recursive template to compute the ith fibonacci number.
*/
template < class T, unsigned i >
struct tmp_fibo {
static const T fibo = tmp_fibo<T,i-1>::fibo + tmp_fibo<T,i-2>::fibo;
};
/* The base cases for i = 1 and i = 0. Partial struct specialization
* is allowed.
*/
template < class T >
struct tmp_fibo<T,1U> {
static const T fibo = (T)1;
};
template < class T >
struct tmp_fibo<T,0U> {
static const T fibo = (T)0;
};
int main(void) {
cout << "fibo(50) = " << tmp_fibo<unsigned long long, 50>::fibo
<< ". fibo(10) = " << tmp_fibo<int, 10>::fibo << "."
<< endl;
return 0;
}
Compile to assembly language, and we see what code the compiler generated for the line tmp_fibo<unsigned long long, 50>::fibo Here it is, in full:
movabsq $12586269025, %rsi
The templates generates an integral constant within each structure at compile-time. What those examples are doing, since you can declare type names within structs, is doing the same thing for types.
I will try to make it simple. Template metaprogramming is about computing types at compile-time (you can also compute values, but just let us focus on that).
So if you have this function:
int f(int a, int b);
You have a function that returns an int value given two int values.
You use it like this:
int val = f(5, 8);
Metafunctions operate on types, instead of on values. A metafunction looks like this:
//The template parameters of the metafunction are the
//equivalent of the parameters of the function
template <class T, class U>
struct meta_f {
typedef /*something here*/ type;
};
Namely, a metafunction has a nested type inside, by convention the nested type is called type.
So you invoke a metafunction like this in non-generic contexts:
using my_new_type = meta_f<int, float>::type;
In generic contexts you must use typename:
using my_new_type = typename meta_f<T, U>::type;
This returns a type, not a run-time value, since we said metafunctions operate on types.
Examples of metafunctions in the standard library can be found in header
type_traits, among others. You have add_pointer<T> or decay<T>. These metafunctions return new types given a type.
In C++14, in order to avoid pieces of code like this, which are verbose:
using my_computed_type = typename std::add_pointer<T>::type;
Some template aliases with a _t suffix, again by convention, were created that invoke the metafunction, directly, for you:
template <class T>
using add_pointer_t = typename std::add_pointer<T>::type;
And now you can write:
using my_computed_type = std::add_pointer_t<T>;
All in all, in a function, you have runtime values as parameters, in a metafunction, the parameters are types. In a function you invoke with the
usual syntax and obtain a runtime value. In a metafunction you get the ::type nested type and get a new computed type.
//Function invocation, a, b, c are values of type A, B, C
auto ret = f(a, b, c);
//Meta function invocation. A, B, C are types
using ret_t = typename meta_f<A, B, C>::type;
//Typical shortcut, equivalent to metafunction invocation.
using ret_t = meta_f_t<A,B,C>;
So for the first function you get a value, for the others, you get a type, not a value.
Related
In c++ std library, is_member_pointer is implemented as
template<typename _Tp>
struct __is_member_pointer_helper
: public false_type { };
template<typename _Tp, typename _Cp>
struct __is_member_pointer_helper<_Tp _Cp::*>
: public true_type { };
/// is_member_pointer
template<typename _Tp>
struct is_member_pointer
: public __is_member_pointer_helper<typename remove_cv<_Tp>::type>::type
{ };
Can someone explain that how the _Cp is deduced? It works like a magic.
The type of a pointer-to-member is Type Class::*, where Type is the object type or function type being pointed to. If you provide the template an int C::*, for example, the compiler can simply deduce the class type by examining the type of the pointer-to-member and seeing the class type is C. It would also deduce the pointed-to type as int the same way. It works very similarly to how we humans would do it. In general, we call this technique pattern matching, which you might be familiar with from regular expressions.
Formally:
[temp.deduct.type]/3.2:
A pointer-to-member type includes the type of the class object pointed to and the type of the member pointed to.
[temp.deduct.type]/8:
A template type argument T, a template template argument TT or a template non-type argument i can be deduced if P and A have one of the following forms:
[snip]
T T::*
[snip]
Where per [temp.deduct.type]/1:
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
Some years ago I basically had the same misunderstanding about C++ template specialization that, apparently, you are having now.
The other answers are great, but I don't think they would have really helped me back then understanding what's going on. So, assuming that you are suffering from the same misunderstanding that I did, let me try to explain how I finally got my thoughts right:
Back then, my intuitive understanding erroneously told me that the term "specialization" means that the "specialized template" should somehow have less template arguments than the "original template". This assumption was driven by the fact that almost every tutorial code that tries to explain how specialization works starts with an example like
template <class T> // <-- one parameter
class MyClass { ... };
template <> // <-- zero parameters
class MyClass<int> { ... };
Now, your example of is_member_pointer is one counterexample showing that this is not true at all.
My entire misunderstanding started with using wrong terminology. You may have noticed that above I put quotes around "specialized template" and "original template"? I did this because it's wrong, but those were the words that I was using back then.
True is, that there is only one template. It's wrong to say that there are two templates, an original one and a specialized one. In my example
template <class T>
class MyClass { ... };
is the template, while
template <>
class MyClass<int> { ... };
is a specialization of that same template.
What makes it a specialization, is the use of <int> behind the class name. That's it!
This is another valid specialization of that same template:
template <class... Types>
struct many_to_one { ... };
template <class A, class B, class C, class D> // <-- four parameters, could be even more
class MyClass<many_to_one<A, B, C, D>> { ... };
As you can see, the specialization has way more template parameters than the actual template. And that's perfectly valid as long as the number of specialization types (one type in this example, namely many_to_one<A, B, C, D>) matches the number of template parameters of the actual template.
Now, what does the compiler do, if you use MyClass<int> anywhere, e.g. for declaring a variable of that type?
It has a look at the template and all specializations of it.
First thing to note: As in this example there is only one template parameter, something like MyClass<int, double, short, float> cannot compile, even though there is a specialization with four parameters! But those four parameters apply to the specialization, not to the template.
When the compiler walks through all the specializations and it finds
template <class A, class B, class C, class D>
class MyClass<many_to_one<A, B, C, D>> { ... };
it has to ask itself "are there any types A, B, C, D so that the given type (int) is equal to the specialization type many_to_one<A, B, C, D>? The answer is No, so it moves on to the next specialization:
template <>
class MyClass<int> { ... };
Again, it asks itself "are there any types <empty list here> so that the given type (int) is equal to the specialization type int? Obviously, yes! Since there are no even better matching specializations, it chooses this one.
If instead you have e.g. MyClass<double>, both questions for the two specializations yield No as the answer, so the "basic" template will be chosen.
So, to finally answer your original question: If you write e.g.
std::is_member_pointer<decltype(&std::string::size)>
the compiler looks at the specialization and sees "Oh, look. If I put _Tp = size_t and _Cp = std::string then the given type decltype(&std::string::size) is equal to the specialization type _Tp _Cp::*, so I pick that specialization (that happens to inherit from std::true_type)".
But if you write e.g.
std::is_member_pointer<int>
then it cannot find any types for _Tp and _Cp to make _Tp _Cp::* equal to int, so it discards that specialization and chooses the "basic" template (that happens to inherit from std::false_type).
There is nothing magical about it. It is simple specialization, and _Cp is deduced to containing class, whenever the template is instantiated for the class member.
It is an application of general case of selecting best available specialization.
I am want a static check of the parameter type of lambdas. I've written this code below and it seems to produce the correct result.
struct B { };
auto lamBc = [](B const& b) { std::cout << "lambda B const" << std::endl; };
template<typename ClosureType, typename R, typename Arg>
constexpr auto ArgType(R (ClosureType::*)(Arg) const)->Arg;
template <typename T>
using ArgType_t = decltype(ArgType(&T::operator()));
// ArgType_t<lamBc> is "reference to B const"
However, I noticed that, for example, the standard library uses class template specialization to extract the referred-to type from a reference type in std::remove_reference. So I tried that approach and it also seems to produce the correct result.
template<typename L>
struct ArgType2;
template<typename ClosureType, typename R, typename Arg>
struct ArgType2<R (ClosureType::*)(Arg) const>
{
typedef Arg type;
};
template <typename T>
using ArgType2_t = typename ArgType2<decltype(&T::operator())>::type;
// ArgType2_t<lamBc> is also "reference to B const"
My questions are: Which is the standard way to extract types from a pattern expression? What are the trade-offs in either approach?
Both your approaches are valid, interchangeable and lead to the same result (deduce the type of the parameter lambda accepts).
For type traits it is required by the standard (see 23.15.1 Requirements) that:
A UnaryTypeTrait describes a property of a type. It shall be a class template that takes one template type argument and, optionally, additional arguments that help define the property being described. ...
A BinaryTypeTrait describes a relationship between two types. It shall be a class template that takes two template type arguments
and, optionally, additional arguments that help define the
relationship being described. ...
A TransformationTrait modifies a property of a type. It shall be a class template that takes one template type argument and, optionally, additional arguments that help define the modification. ...
I suppose that this requirement appeared mostly for historical reasons as decltype functionality was introduced after type traits had been proposed (and these type traits were based on type traits from boost which had been created even earlier, see, for example, this).
Also, note, that class templates are more flexible for general purpose type traits than the logic based on functions declarations and decltype.
The main point is that with C++11 and later in your particular case you are free to use the way which is the most convenient and reflects the programming logic better.
They are both "standard", the decltype() way is simply available in modern C++ variants, the PTS method was the only way to do this sort of things under the original C++ standard.
I'm in trouble for the way I should reference arguments in a template (honestly, I strongly suspect that it's not possible to achieve what I'd like to do).
It follows an example of what I'd like to do (of course, this is not syntactically legal, the aim is to give the idea of which is the target):
template<class C, Ret(C::*Member)(Params...), typename Ret, typename... Params>
class MyClass { }
In other terms, I'd like to reference a member of a class, by specifying at the same time also which is the returned value and the parameters of that method.
Unfortunately, the only way I see to do that is something like the following one (well, it depends indeed on where those typenames are required, anyway it may be a meaningful example):
template<typename Ret, typename... Params>
class MyClass {
public:
template<class C, Ret(C::*Member)(Params...)>
MyClass(C *c) { /* do something else and give sense to this class */ }
}
Besides the one above, that is to break the interlacing by introducing a templated constructor, there exists another valid approach to obtain the same result with the sole class template signature?
I know (pretty simple) how to achieve that in case of not variadic template (as an example, move Ret before Member), but the variadic one (Params) has to lay at the end of the template list and I cannot refer it in any way.
According with this question, a viable solution could be to rely on deduction forced by a default value.
As an example, the following code should be valid:
class MyClass {
public:
template <class C, typename R, typename... P, R(C::*M)(P...) = &C::foo>
void bar(C *c) { }
};
I cite part of the linked question (a citation that is a citation for itself, I'm in a loop):
A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument.
Because of that, the following code should not be allowed instead, even if it compiles with GCC:
class MyClass {
public:
template <class C, typename R, typename... P, R(C::*M)(P...)>
void bar(C *c) { }
};
Well, quite tricky, not so flexible a solution and honestly I ended up far ago with a bit of refactoring, but for the sake of clarity I've decided to add an answer and close the question with a snippet that compiles.
I'm following this great tutorial. The author heavily uses variadic templates and I came to a point where I'm stuck, can't understand. Can you help me?
1. Why isn't this compiling?
// this is simple
template<size_t I, typename T>
struct tuple_element
{
T value;
};
// this does NOT compiles: error: parameter pack 'Indices' must be at the end of the template parameter list
template <size_t... Indices, typename... Types>
struct tuple_impl : tuple_element<Indices, Types>...
{
};
Next, the author have this code that compiles fine:
template <size_t... Indices>
struct index_sequence
{
using type = index_sequence<Indices...>;
};
template <typename Sequence, typename... Types>
struct tuple_impl;
template <size_t... Indices, typename... Types>
struct tuple_impl<index_sequence<Indices...>, Types...>
: tuple_element<Indices, Types>...
{
};
2. Why in this case everything ok? I see almost the same pattern here: tuple_element<Indices, Types>...
3. Why this can't be compiled:
template <size_t... Indices>
void g(Indices...){}; //error: variable or field 'g' declared void
The error you are seeing is in the passing of parameters. This is a compile error:
template <size_t... Indices, typename... Types>
struct tuple_impl
{};
live example
The rule is you cannot have one pack followed by another in a template class template parameter list.
The second example is a specialization, where the pack rule does not exist. The template parameters of a specialization are merely the types and values which are extracted from the pattern matching against the primary templates, as guided by the <> portion of the specialization after the type name.
As they are never passed in in that same list & order, having one ... after another doesn't cause any ambiguity. With a primary template, the order matters, and anything after a ... is difficult to distinguish from more of the .... Probably to keep the compiler's job easier, even in cases where it cannot be ambiguous (say, a pack of literals followed by a pack of types), C++ bans its use.
The compiler would have no means to distinguish when the first sequence ends and the second starts - therefore it is allowed to have only one parameter pack in variadic templates, at the end.
tuple_impl<a, b, c, d, e, f> //is d still an index or already a type? what about e?
This works, because tuple_impl itself is a template that has itself only one parameter pack, the Types.... It just happes that in this specialization the first parameter is a template, too, which has a parameter pack, too. So, in contrast to one template with two packs, you have two templates with one pack each, which is ok.
This does not have to do with variadic templates, i.e. it would not work with a single argument either, for the same reason. The fact is, that since Indices... are not types but values, you are not defining a function. If it was not void, the compiler would have had problems later. Consider this example, which is slightly modified but essentially a similar construct:
template <size_t I>
unsigned g(I)
{}
The middle line is central: The compiler thinks that it is a variable definition, initialized with I. Therefore the error in your case, because variables of type void simply don't make sense. My compiler then emits a warning about the template, it thinks that g is a templated variable, and those are a C++1y extension. After that is done, he realizes the variable definition is not finished by a ;, emits an error and exits...
Just to add some important thing: this is a test snippet:
int main( int argc, char** argv )
{
using i3 = index_sequence<1, 2, 3>;
tuple_impl<i3, int, double, char> tup;
return 0;
}
Note: here you pass this i3 as the "index pack". The "master template" always defines how the parameters have to be passed to the template. The template<...> statement if set for specialization does not define anything, but just what internally the parameter combination may spread inside the specialization, but it's not a part of public interface.
For example, if you try to use <1, 2, 3, int, double, char> as a parameter specification (which would theoretically match the template parameter specification for the specialization), it will fail to compile.
I was reading the Wikipedia article on SFINAE and encountered following code sample:
struct Test
{
typedef int Type;
};
template < typename T >
void f( typename T::Type ) {} // definition #1
template < typename T >
void f( T ) {} // definition #2
void foo()
{
f< Test > ( 10 ); //call #1
f< int > ( 10 ); //call #2 without error thanks to SFINAE
}
Now I've actually written code like this before, and somehow intuitively I knew that I needed to type "typename T" instead of just "T". However, it would be nice to know the actual logic behind it. Anyone care to explain?
The short version that you need to do typename X::Y whenever X is or depends on a template parameter. Until X is known, the compiler can't tell if Y is a type or a value. So you have to add typename to specify that it is a type.
For example:
template <typename T>
struct Foo {
typename T::some_type x; // T is a template parameter. `some_type` may or may not exist depending on what type T is.
};
template <typename T>
struct Foo {
typename some_template<T>::some_type x; // `some_template` may or may not have a `some_type` member, depending on which specialization is used when it is instantiated for type `T`
};
As sbi points out in the comments, the cause of the ambiguity is that Y might be a static member, an enum or a function. Without knowing the type of X, we can't tell.
The standard specifies that the compiler should assume it is a value unless it is explicitly labelled a type by using the typename keyword.
And it sounds like the commenters really want me to mention another related case as well: ;)
If the dependant name is a function member template, and you call it with an explicit template argument (foo.bar<int>(), for example), you have to add the template keyword before the function name, as in foo.template bar<int>().
The reason for this is that without the template keyword, the compiler assumes that bar is a value, and you wish to invoke the less than operator (operator<) on it.
In general, C++'s syntax (inherited from C) has a technical defect: the parser MUST know whether something names a type, or not, otherwise it just can't solve certain ambiguities (e.g., is X * Y a multiplication, or the declaration of a pointer Y to objects of type X? it all depends on whether X names a type...!-). The typename "adjective" lets you make that perfectly clear and explicit when needed (which, as another answer mentions, is typical when template parameters are involved;-).
Basically, you need the typename keyword when you are writing template code (i.e. you are in a function template or class template) and you are referring to an indentifier that depends on a template parameter that might not be known to be a type, but must be interpreted as a type in your template code.
In your example, you use typename T::Type at definition #1 because T::Type depends on the template parameter T and might otherwise be a data member.
You don't need typename T at definition #2 as T is declared to be a type as part of the template definition.