How is template metaprogramming working here (static const int value = 1 + StarCounter<\U>::value;) to print out 3 ?
#include <iostream>
template <typename T>
struct StarCounter
{
static const int value = 0;
};
template <typename U>
struct StarCounter<U*>
{
static const int value = 1 + StarCounter<U>::value;
};
int main()
{
std::cout << StarCounter<int***>::value << std::endl;//How is it printing 3?
return 0;
}
The first template creates a struct that will always return 0 when you call StarCounter<U>::value.
The second template specialises the first one for cases where a pointer is used. So when you call it with StarCounter<U*>::value, the second template is used, not the first and it will return StarCounter<U>::value + 1. Note that it removes the pointer at each recursion step.
So the call to StarCounter<int***>::value will expend to:
StarCounter<int***>::value // second template is used due to pointer
1 + StarCounter<int**>::value // second template is used due to pointer
1 + 1 + StarCounter<int*>::value // second template is used due to pointer
1 + 1 + 1 + StarCounter<int>::value // no pointer here, so first template is used
1 + 1 + 1 + 0
3
StarCounter<int>::value
equals 0, because it's matched with first instantiation of the template, where value is explicitly defined.
StarCounter<int*>::value = 1 + StarCounter<int>::value
equals 1, because StarCounter<int*> is matched with StarCounter<U*>. Yes, StarCounter<T> can also be considered as a match, but StarCounter<U*> is more specific and that's why this one is preferred.
Similarly,
StarCounter<int**>::value = 1 + StarCounter<int*>::value
equals 2 and
StarCounter<int***>::value = 1 + StarCounter<int**>::value
equals 3.
I find it helps to think of runtime equivalents when it comes to metaprogramming. In template metaprogramming, we use partial specialization, as in runtime programming, we use recursion. The primary template functions as the base case and the specializations function as the recursive cases.
Consider the following recursive version of determining the size of a container:
def size(x):
if empty(x):
return 0
else:
return 1 + size(tail(x))
This is the equivalent of the template code you present. The primary template, StarCounter<T>, is the base case. The empty case. It has size (value) zero. The specialization, StarCounter<U*>, is the recursive case. It has size (value) 1 plus the size of recursing with the tail (StarCounter<U>).
In C++17, we can even more explicitly make the metaprogramming version equivalent to the runtime recursive version (this is presented solely as an example and not as a way that this code should be written):
template <class T>
struct StarCounter {
static constexpr int calc_value() {
if constexpr (!std::is_pointer<T>::value) {
return 0;
}
else {
return 1 + StarCounter<std::remove_pointer_t<T>>::value;
}
}
static constexpr int value = calc_value();
};
There is a template StarCounter which in it's more general form has constant value equal to 0. So when you use this template for most of the types and ask for the value you will get 0.
This template has also a specialized version which accepts pointers.
It's implementation also has constant value which is equal to 1 (as we have a pointer which means we have at least one star) plus value of value of type which this pointer points to.
In case of three stars we have:
StarCounter<int***>::value = 1 + StarCounter<int**>::value (1) + StarCounter<int*>::value (1) + StarCounter<int>::value (0)
Related
On the cppreference article about SFINAE, I came across this syntax which I do not understand:
template <int I> void div(char(*)[I % 2 == 0] = 0) {
// this overload is selected when I is even
}
template <int I> void div(char(*)[I % 2 == 1] = 0) {
// this overload is selected when I is odd
}
I get that its a template function declaration, but I don't understand what the parameters mean. In particular, what are the square brackets in char(*)[I % 2 == 0] = 0 and why is it equal to 0?
Arrays of size 0 are illegal in C++ so a particular overload is chosen based on the value of I: when I is 5, char(*)[I % 2 == 0] would be a pointer to an array of size false, i.e. zero, while the other array pointer is perfectly legal.
= 0 is just a default ignored argument value to save you from typing (0) every time calling this function.
In C++20, it would be much more apparent and straightforward:
template<int I> requires (I % 2 == 0) void div() {
}
And even in C++17:
template<int I> void div() {
if constexpr(I % 2 == 0) {
}
else {
}
}
When I is even then you have (remember that false converts to 0 and true to 1):
template <int I> void div(char(*)[1] = 0) {
// this overload is selected when I is even
}
template <int I> void div(char(*)[0] = 0) {
// this overload is selected when I is odd
}
The second attempts to use an array of size 0 and according to the bullet point on that page:
The following type errors are SFINAE errors:
attempting to create an array of void, array of reference, array of function, array of
negative size, array of non-integral size, or array of size zero.
... that triggers SFINAE and the first overload is selected. The array size is 0 for the first overload when I is odd. Note that the parameter is unnamed. It is not used inside the function, its only purpose is to trigger SFINAE. Calling this function you need not pass the parameter because it has a default, thats the =0. Passing a parameter would be useless because its value isn't used anyhow.
This is a rather arcane example of SFINAE. Since C++11 you would perhaps use std::enable_if together with a template <int I> is_odd; or similar and starting from C++20 you would use concepts.
I am writing a simple test program using TMP to calculate the Nth fibonacci number. I have already found many ways to do this, but I'm just trying out a bunch of ways to get my understanding better. The way I am having a problem with is this:
template<int A>
struct fib
{
static const bool value = (A<2);
static const int num = (value?A:(fib<A-1>::num + fib<A-2>::num));
};
The error message I am getting is:
error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) instantiating 'fib<-1796>::value'|
I have tried substituting many values into the "false" field of the ternary, just to play with it and see what it does. I still do not understand why this does not work. Can anyone help me and tell me why? Thanks.
EDIT: My guess is that the compiler might be evaluating the T/F fields of the ternary before checking to see if the value is true or false, but I'm not sure since that's not how an if statement is supposed to work at all, and these are supposed to roughly emulate if statements
I must admit that I'm not that experienced concerning template programming. But in OP's case, a simple solution would be template specialization.
Sample code:
#include <iostream>
template<int A>
struct fib
{
static const int num = fib<A-1>::num + fib<A-2>::num;
};
template<>
struct fib<1>
{
static const int num = 1;
};
template<>
struct fib<2>
{
static const int num = 1;
};
int main()
{
fib<10> fib10;
std::cout << "fib<10>: " << fib10.num << '\n';
return 0;
}
Output:
fib<10>: 55
Live Demo on coliru
One way to write this in a more straightforward manner is to use if constexpr. Unlike with regular if (and with the ternary operator), templates in the not taken branch are not instantiated.
template <int n>
struct fib {
constexpr static int eval() {
if constexpr (n < 2)
return n;
else
return fib<n-1>::eval() + fib<n-2>::eval();
}
};
Of course once you have if constexpr you don't really need templates to make a compile-time function of this type. A constexpr non-template function will do just fine. This is just an illustration of the technique.
The first comment on my original post is the correct answer. The author is (n.m.). Template type instantiations are evaluated in both fields of the ternary, while the object instantiation itself is not. To solve this, I will look into std::condiditional or std::enable_if
I think I understand how templates are evaluated lazily in C++ e.g. a la recursive replacements and a final simplification of the expansion. This typically limits the recursion depth available. I wonder if with the features new in C++11 (e.g. variadic templates or template packs) or with some Boost it is possible to force strict template evaluation. Or is this in principle impossible in C++?
Consider for example a template which sums all integer values 0..n:
template <int n>
struct sumAll { enum { value = n + sumAll<n-1>::value }; };
template <>
struct sumAll<0> { enum { value = 0 }; };
#include <iostream>
int main() { std::cout << sumAll<10000>::value << std::endl; }
Here sumAll<10>::value would be expanded to
sumAll<10>::value = 10 + sumAll<9>::value
= 10 + 9 + sumAll<8>::value
= 10 + 9 + 8 + sumAll<7>::value
= ...
= 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0
and the final summation would only be performed once the template has been completely expanded. If that final expansion gets too long (e.g. in complex series expansions with many terms) the compiler will ultimately run out of space to store additional terms.
My question was in essence if there was a way to perform simplifications (like above summation) earlier.
You decide the recursion depth yourself. And just like normal recursion can cause stack overflows, template recursion can. But that's often fixable by a better recursive algorithm. Trivially:
template <int n>
struct sumAll { enum { value = n + n-1 + sumAll<n-2>::value }; };
template <>
struct sumAll<1> { enum { value = 0 }; };
template <>
struct sumAll<0> { enum { value = 0 }; };
Smarter:
template <int n>
struct sumAll { enum { value = (n*n+2)/2; };
Of course, you may complain that the latter is just being silly and real examples are more complex. But isn't that the whole problem? The compiler can't magically make that complexity go away for you.
C++ templates are turing-complete, which means that you use them to evaluate every computable function at compile time. It then follows from the halting theorem that
You cannot, in general, compute the amount of memory require to compile of a C++ program in advance. (I.e., there is no computable function which maps every C++ program to a memory bound for its compilation)
You cannot, in general, decide whether the compiler will ever finish instantiating template, or will go on forever.
So while you might be able to tweak a compiler to use less memory in some cases, you cannot solve the general problem of it running out of memory sometimes.
This is a more specific question based on a question I asked earlier...
If I have a function that takes two parameters (one required, one optional):
an STL container such as a vector
an "optional" comparison function that serves as a relational overload and returns the maximum value, whatever that is, in the vector...
Code:
template <typename Type>
Type FindMax (std::vector<Type> &myVec, int (*cmp)(Type one, Type two) = CallBack)
/.../
WHAT exactly, does the "int (*cmp)(Type one...)" say to the compiler? I want it to say, here's a function to use when comparing two of type Type...ie when using the relational operators <, >, =, etc. If no function is supplied by the user then use the default, otherwise, use what the user provides...
What exactly does the (*cmp)(Type one, Type two) say? Here's a pointer to a function that takes two parameters Type one and Type two? Is there any significance as to what comes after the *, ie could I write (*titsmagee)(Type one, Type two)? I'm assuming the naming convention is to help future readers?
For this to work with a "struct" does anything specific to the potential comparisons to be made need to be stored within said struct?
Thanks!
int (*cmp)(Type one, Type two)
The parameter named cmp is a pointer to a function returning int that takes two parameters of type Type.
struct Foo
{
int x;
};
int compare(Foo x, Foo y)
{
return (x.x == y.x) ? 0 : (x.x > y.x ? 1 : -1);
}
std::vector<Foo> vec;
FindMax<Foo>(vec, &compare);
You need cmp so you can call the comparison function inside FindMax:
template <typename Type>
Type FindMax (std::vector<Type> &myVec, int (*cmp)(Type one, Type two) = CallBack)
{
//whatever loop
max = cmp(myVec[i],myVec[j]) >= 0 ? myVec[i] : myVec[j];
}
EDIT Breaking down the return:
return (x.x == y.x) ? 0 : (x.x > y.x ? 1 : -1);
?: is the ternary conditional operator.
condition ? expression1 : expression2
returns (loosely speaking) expression1 if condition is true, expression2 otherwise.
So what that means is:
if (x.x == y.x)
return 0;
else
if (x.x > y.x)
return 1;
else
return -1;
It's what you expect the comparison function to do. Return 0 for equality, 1 if the first element is bigger than the second, and -1 for the inverse.
EDIT 2
struct Foo
{
int x;
};
//Foo has a member x.
Foo f;
//Create a Foo object called f.
f.x;
//Access the member x of the object
I am getting this error and another error too ** "IntelliSense: no instance of function template matches the argument list"** when compiling the following code
I know there might be logic mistakes in my function but I need to solve this error first to be able to debug my function .
#include <iostream>
using namespace std;
template<class T>
T myMax (T& arr ,int arrStart ,int arrSize)
{
if(arrStart==arrSize-1)
return arr[arrSize];
int median = (arrStart+arrSize)/2 ;
T left , right , maximum ;
left = max(arr,arrStart , median);
right = max(arr , median+1 , arrSize-1) ;
if (left>right)
maximum = left;
else
maximum = right ;
return maximum ;
}
void main()
{
int arrSize = 5;
int arr[] = {1,3,4,6,22};
int x;
x = myMax(arr,0,arrSize);
}
The argument for parameter arr is of type int[5]. Since you didn't specify a template argument for T when calling myMax, argument deduction happens and T is deduced to be int[5].
Then the compiler attempts to specialize the function template with T = int[5] (i.e., it tries to replace all instances of T with int[5]). This fails because the function returns a T by value but it is not possible to return an array (like int[5]) by value.
It looks like you want T to be the element type. If that is the case, you can explicitly take a reference to the array, e.g.,
template<class T, unsigned N>
T myMax (T (&arr)[N])
Though, a more idiomatic way to write the function would be to have it take a pair of random access iterators and have it return an iterator pointing to the "max" element:
template <typename RandomAccessIt>
RandomAccessIt myMax (RandomAccessIt first, RandomAccessIt last)
first is an iterator to the first element in the range and last is an iterator to one-past-the-end of the range, as is idiomatic for the STL algorithms. Pointers are usable as random access iterators, so this function can be called as
int* pointerToMaxElement = myMax(arr, arr + arrSize);
The advantage of the iterator approach is that it works with any random access range, including an array, std::vector, std::array, and std::deque.
From a quick look, the two things that jump out at me are:
You're using T in different ways in the template function. You're returning a T object, and taking a reference to a T object as an argument - but when you use it, you're passing an an int array as the argument but expect just an int returned
You don't call your template function with any template (ie, myMax<int>(...)) Edit - as Mark B points out, this isn't required however