First FAQ post
Check it out!!
http://surprising-code.blogspot.com/p/pointers-to-member-functions.html
Wednesday, February 29, 2012
Tuesday, February 28, 2012
Nontype Template Parameters
Konnichiwa!!
Lately I have been very excited programming and I really have to do an effort to continue chapter after chapter in order not to mess up the blog. However, if you are interested to get further feel free to check out the examples page.
So then, let's work!
First off, what's the meaning of nontype template parameters? Indeed, the idea is so simple, is just to use types like int inside templates. Till now, all template examples we have seen has been with the reserve word typename or class but the powerful of templates don't finish here.
For example, a Matrix:
template<int W, int H>
struct Matrix{
double m[W][H];
};
Thus, now, we can simply constructor of a Matrix like this:
Matrix<4,4> A={0,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1};
Matrix<4,1> B={1,2,3,4};
In the case using a class in lieu of a struct, we could not do it in this way unless we started using C++11.
Another example usefull with matrices could be the below:
template<int sizeAX,int sizeAY, int sizeBY>
void mult(const Matrix<sizeAX,sizeAY>& A,const Matrix<sizeAY,sizeBY>& B,
Matrix<sizeAX,sizeBY>& C);
Note: I'm using pass by reference, check out FAQ if dont get it at all why I used &'s or const.This function multipy does A · B = C
But not always matrices can be multiplied. With the definition we did, we can do A · B but not B · A.
In this case we'll have a compile error:
(I change the variable names to make it more understandable)
void mult(const Matrix<4,1>& A,const Matrix<4,4>& B,
void mult(const Matrix<sizeAX,sizeAY>& A,const Matrix<sizeAY,sizeBY>& B,
sizeAX = 4; sizeAY = 4;
sizeAY = 1; sizeBY = 4;
SizeAY don't match so here is where the error occurs. But, in fact, is better to have this error in compile time than runtime.
Struct Matrix here!!
A similar example, here:
template<typename T, int ROWS, int COLS>
class Matrix;
template<int InCOLS>
Matrix<T, ROWS, InCOLS> operator*(const Matrix<T, COLS, InCOLS>& in) const;
If template COLS of our matrix match with the rows of the supose matrix to be multiply then we are able to do it. And the result of this multiply will be ROWS and InCOLS. Slowly: A(3,2)*B(2*10) = C(3,10) If colsA(2) is equal to rowsB(2). sizeC = (rowsA, colsB)
Monday, February 27, 2012
Summary
A short summary.
So far, I have introduced templates programming step by step with some examples. I didn't explain many things by myself, I tried to show you how it works.
Bearing all of this in mind, it's time to go over the profits of templates and the reason of being so interesant.
So far, I have introduced templates programming step by step with some examples. I didn't explain many things by myself, I tried to show you how it works.
Bearing all of this in mind, it's time to go over the profits of templates and the reason of being so interesant.
- Templates are type-safe: The compiler is able to do the type checking before a error occurs.
- Easiness writing them: With just one class we may have an infinity number of uses
Remember the example of max, with which one we saved many definitions.
- Optimize the runtime by compiling time: This point, I'd like to flesh out through metaprogramming but stay with the idea.
- Good possibility to reuse code.
Labels:
why templates
Sunday, February 26, 2012
Default Template Arguments
Hello everybody, sorry for the delay. I have been so busy although I posted some examples.
Some chapters before we defined a Queue class. Now, we retake it.
template<typename T, typename CONT = std::vector<T> >
T, like previous examples, is the type that contain our class. And CONT is the template parameter that the class will use as container.
For example:
Using the new class Queue ( at the end you the sheet or here ):
Queue<int> queueInt;
Queue<int, std::deque<int> > queueIntWithDeque;
So, we can indicate the container that our queue will use or not. Taking the dafault argument in this case.
Even, we can define a default type, for example:
template<typename T = double, typename CONT = std::vector<T> >
And now, we can use the default templates, for instance:
Queue<> defaultExample;
Another example which I will explain deeper in the next chapter are the nontype template parameters.
Basically, we are also able to define these as default.
A short example:
template<int H = 2, int W = 2 > (example of nontype template parameters here)
Okey, let's see the new class Queue:
output:
(1, 4)with size:4
(2, 4)with size:3
(3, 4)with size:2
(4, 4)with size:1
(15, 15)with size:1
(10, 40)with size:4
(20, 40)with size:3
(30, 40)with size:2
(40, 40)with size:1
(150, 150)with size:1
complet dafault example
(1.3, 4.4)with size:4
(2.2, 4.4)with size:3
(3.1, 4.4)with size:2
(4.4, 4.4)with size:1
(150, 150)with size:1
RUN SUCCESSFUL (total time: 615ms)
However, default templates have their own shortcomings, for example, in this case, we did the Queue methods using functions like: back(), front(), ...
All this methods has to be implemented in the container we are going to use. In the case we didn't, we'll get some errors in time compiling (fortunately).
Templates are so useful for this, you can check errors in compiling time. If you saw the example of a Matrix class that I did, you can see this feature. We can multiply matrixes, and at compiling time, we can be sure all the products are possible.
Enjoy it!
Today, I'll show you an useful tool with templates. You will able to define default templates, this, it is so interested especially for containers.
Some chapters before we defined a Queue class. Now, we retake it.
How it looks a Default Template:
T, like previous examples, is the type that contain our class. And CONT is the template parameter that the class will use as container.
For example:
Using the new class Queue ( at the end you the sheet or here ):
Queue<int, std::deque<int> > queueIntWithDeque;
So, we can indicate the container that our queue will use or not. Taking the dafault argument in this case.
Even, we can define a default type, for example:
template<typename T = double, typename CONT = std::vector<T> >
And now, we can use the default templates, for instance:
Queue<> defaultExample;
Another example which I will explain deeper in the next chapter are the nontype template parameters.
Basically, we are also able to define these as default.
A short example:
template<int H = 2, int W = 2 > (example of nontype template parameters here)
Okey, let's see the new class Queue:
#include <iostream> #include <stdexcept> #include <vector> #include <deque> template <typename T = double, typename CONT = std::vector<T> > class Queue{ private: CONT elements; public: Queue(){}//default constructor T back(){ // return last element return elements.back(); } void push(const T& element){//add an element elements.push_back(element); } void pop(){//erase first element if (elements.empty()) { throw std::out_of_range("Stack<>::pop(): empty stack"); } elements.erase( elements.begin() ); } bool empty(){return elements.empty();} int size(){return elements.size(); } T front(){ if (elements.empty()) { throw std::out_of_range("Stack<>::pop(): empty stack"); } return elements.front();} }; template< typename T> void iterate_Queue(Queue<T>& queue){ while(not queue.empty()) { std::cout << "(" << queue.front()<< ", " << queue.back() << ")"; std::cout << "with size:"<< queue.size() << std::endl; queue.pop(); } std::cout << std::endl; }
// overloading function template< typename T, typename CONT> void iterate_Queue(Queue<T,CONT>& queue){ while(not queue.empty()) { std::cout << "(" << queue.front()<< ", " << queue.back() << ")"; std::cout << "with size:"<< queue.size() << std::endl; queue.pop(); } std::cout << std::endl; } typedef Queue<int> intQueue;// It's common to define typedef's in order to simply int main() { try{ intQueue y; y.push(1); y.push(2); y.push(3); y.push(4); iterate_Queue(y); y.push(15); iterate_Queue(y); Queue<int, std::deque<int> > newExample; newExample.push(10);
newExample.push(20);
newExample.push(30);
newExample.push(40);
iterate_Queue(newExample);
newExample.push(150);
iterate_Queue(newExample);
std::cout << "complet dafault example " << std::endl;
Queue<> defaultExample;
defaultExample.push(1.3);
defaultExample.push(2.2);
defaultExample.push(3.1);
defaultExample.push(4.4);
iterate_Queue(defaultExample);
defaultExample.push(150);
iterate_Queue(defaultExample);
} catch (const std::exception& ex) { std::cerr << "Exception: " << ex.what() << std::endl; } return 0; }
output:
(1, 4)with size:4
(2, 4)with size:3
(3, 4)with size:2
(4, 4)with size:1
(15, 15)with size:1
(10, 40)with size:4
(20, 40)with size:3
(30, 40)with size:2
(40, 40)with size:1
(150, 150)with size:1
complet dafault example
(1.3, 4.4)with size:4
(2.2, 4.4)with size:3
(3.1, 4.4)with size:2
(4.4, 4.4)with size:1
(150, 150)with size:1
RUN SUCCESSFUL (total time: 615ms)
However, default templates have their own shortcomings, for example, in this case, we did the Queue methods using functions like: back(), front(), ...
All this methods has to be implemented in the container we are going to use. In the case we didn't, we'll get some errors in time compiling (fortunately).
Templates are so useful for this, you can check errors in compiling time. If you saw the example of a Matrix class that I did, you can see this feature. We can multiply matrixes, and at compiling time, we can be sure all the products are possible.
Enjoy it!
Tuesday, February 21, 2012
Specializations
Morning!
Today, I want to show the specialize of classes. Thereby, you can give a diferent implementation.
Templates are so useful but sometimes could be necessary to make exceptions. C++ provide this tool that is similar to override functions.
Testing specialitzations:
Today, I want to show the specialize of classes. Thereby, you can give a diferent implementation.
Templates are so useful but sometimes could be necessary to make exceptions. C++ provide this tool that is similar to override functions.
Testing specialitzations:
#include <iostream> //base case or primary template template <typename T1, typename T2> class Example{ private: T1 _element1; T2 _element2; public: Example(){ std::cout << "Example" << std::endl; } }; //Specializations or also called partial or full specialization //depending on each case template <typename T> class Example<T, T>{ private: int _element1; int _element2; public: Example(){ std::cout << "Example same class" << std::endl; } }; template <typename T> class Example<T, int>{ private: int _element1; int _element2; public: Example(){ std::cout << "Example template and int" << std::endl; } }; template <typename T1,typename T2> class Example<T1*,T2*>{ private: int _element1; int _element2; public: Example(){ std::cout << "Example template* and template* " << std::endl; } }; int main(){ Example<int,float> ex1; Example<float,float> ex2; Example<float,int> ex3; Example<int*,float*> ex4; //Exampleex5; // error: /* * main.cpp:57:22: error: ambiguous class template instantiation for ‘class Example<int, int>’ * main.cpp:19:20: error: candidates are: class Example<T, T> * main.cpp:31:22: error: class Example<T, int> * * To fix it we might specify: * * template <> * class Example<int,int> */ //Exampleex6; // error: /* * main.cpp:64:24: error: ambiguous class template instantiation for ‘class Example<int*, int*>’ * main.cpp:19:20: error: candidates are: class Example<T, T> * main.cpp:42:23: error: class Example<T1*, T2*> * * To solve this issue we could implement a specialitzacion for pointers * of the same type: * * template<typename T> * Example<T*, T*> * */ }
Sunday, February 19, 2012
First template class, queue
Hello again!
I hope you have started to see the useful of templates, till now, I only showed templates with functions but, of course, that was just the start.
Now, I'll begin showing template classes. One of the most famous examples to show this are container classes, specially Stack. To change, I have done a Queue.
By the way, I have programmed using the exception library, if you don't get it, just ignored and think you could implement with other ways.
This way programming is so common in Java and if you come from it should be so easy to you.
First of all, what is the behave of a queue? just think with the queue of a cinema. First in, first out.
Example of the behave having this queue: [ a, b, c, d ]
With the back function we get d.
With a push(e) we'll have [ a, b, c, d, e ]
With pop(): [ b, c, d, e ]
empty(): false
size(): 4
front(): b
Well, here you have the code Note that I used a STL container internally.
Ok, I hope you understood the most code, let's take a look of hard parts.
First, I did a template function to empty and show the values of a Queue, printing the first and the last element.
Beside, we have some exceptions (throws) in our class, pop, back and front. Theses exceptions void the problem to access an empty vector, in that case, we make a throw:
I hope you have started to see the useful of templates, till now, I only showed templates with functions but, of course, that was just the start.
Now, I'll begin showing template classes. One of the most famous examples to show this are container classes, specially Stack. To change, I have done a Queue.
By the way, I have programmed using the exception library, if you don't get it, just ignored and think you could implement with other ways.
This way programming is so common in Java and if you come from it should be so easy to you.
First of all, what is the behave of a queue? just think with the queue of a cinema. First in, first out.
Example of the behave having this queue: [ a, b, c, d ]
With the back function we get d.
With a push(e) we'll have [ a, b, c, d, e ]
With pop(): [ b, c, d, e ]
empty(): false
size(): 4
front(): b
Well, here you have the code Note that I used a STL container internally.
#include <iostream>
#include <stdexcept>
#include <vector>
template <typename T>
class Queue{
private:
std::vector<T> _elements;
public:
Queue(){}//default constructor
T back(){ // return last element
if (_elements.empty()) {
throw std::out_of_range("Queue<>::back(): empty stack");
}
return _elements.back();
}
void push(const T& element){//add an element
_elements.push_back(element);
}
void pop(){//erase first element
if (_elements.empty()) {
throw std::out_of_range("Queue<>::pop(): empty stack");
}
_elements.erase( _elements.begin() );
}
bool empty(){return _elements.empty();}
int size(){return _elements.size();}
T front(){
if (_elements.empty()) {
throw std::out_of_range("Queue<>::front(): empty stack");
}
return _elements.front();}
};
template< class T >
//Note that we are passing by reference, so original queue will be erased
void iterate_Queue(Queue<T>& queue){
while(
not queue.empty()) {
std::cout << "(" << queue.front()<< ", " << queue.back() << ")";
std::cout << "with size:"<< queue.size() << std::endl;
queue.pop();
}
std::cout << std::endl;
}
typedef Queue<int> intQueue;// It's common to define typedef's in order to simply
//definicions
//typedef Queue< intQueue > intQueueQueue
;
typedef Queue< Queue<int> > intQueueQ
ueue;
// ^-- This space isn't opcional, copiler'd think that is a << operador
int main() {
try{
intQueue y;
y.push(1);
y.push(2);
y.push(3);
y.push(4);
iterate_Queue(y);
y.push(15);
iterate_Queue(y);
y.front();//exception, queue is empty
} catch (const std::exception& ex) {
std::cerr << "Exception: " << ex.what() << std::endl;
}
return 0;
}
output:
(1, 4)with size:4
(2, 4)with size:3
(3, 4)with size:2
(4, 4)with size:1
(15, 15)with size:1
Exception: Queue<>::front(): empty stack
RUN SUCCESSFUL (total time: 244ms)
Ok, I hope you understood the most code, let's take a look of hard parts.
First, I did a template function to empty and show the values of a Queue, printing the first and the last element.
Beside, we have some exceptions (throws) in our class, pop, back and front. Theses exceptions void the problem to access an empty vector, in that case, we make a throw:
throw std::out_of_range("Queue<>::pop(): empty stack");
Then, in the main, we bracketed our code with a try and catch. This means that C++ is going ahead taking care whether an error happends and if so then execute the catch. In fact, that error is called by the throw we made.
In the catch, ex.what() get the string we threw.
Another example of throw:
throw std::overflow_error("overflow");
even:
throw 20;//in that case you have to catch an int
On the other hand, I introduced typedef operator in which you can define any type as you like:
//typedef Queue< intQueue > intQueueQueue
;
typedef Queue< Queue<int> > intQueueQueue;
typedef Queue< std::vector<int
> > intVectorQ
ueue;I hope you enjoyed. For any doubt or comment, feel free to leave your message.
For an example without use of std::vector :
http://surprising-code.blogspot.com/p/example8.html
Saturday, February 18, 2012
Overloading: calls by reference and by value
In this chapter you would understand the significance defining functions types.
By the way, it's a good idea to pass all arguments by references whenever you don't have to modify any parameter. In fact, pass by references is much efficient.
Example:
By the way, it's a good idea to pass all arguments by references whenever you don't have to modify any parameter. In fact, pass by references is much efficient.
Example:
#include <iostream>
#include <cstring>
// maximum of two values of any type (call-by-reference)
template <typename T>
inline T const& max(T const& a, T const& b) {
std::cout << "call-by-reference, 2 arguments" << std::endl;
return a < b ? b : a;
}
// maximum of two C-strings (call-by-value)
inline char const* max(char const* a, char const* b) {
std::cout << "call-by-value" << std::endl;
return (std::strcmp(a, b) < 0) ? b : a;
}
// maximum of three values of any type (call-by-reference)
template <typename T>
inline T const& max(T const& a, T const& b, T const& c) {
std::cout << "call-by-reference, 3 arguments" << std::endl;
return max(max(a, b), c); // error, if max(a,b) uses call-by-value
}
int main() {
::max(1, 2, 3);// OK
std::cout << std::endl;
const char* s1 = "surpring";
const char* s2 = "-";
const char* s3 = "code";
::max(s1, s2, s3);// ERROR, warning: returning reference to temporary
std::cout << std::endl;
::max(*s1, *s2, *s3);//works good! (by reference)
std::cout << std::endl;
}
::max(s1, s2, s3);// ERROR, warning: returning reference to temporary
This line is given an error/warning because call to the non-template function andhis parameters expected a value not a reference.
We may fix up it changing the definition to const char* const&. (There are others)
If we switch the order the compiler might take another function, for example, if the order is the bellow:
template <typename T> inline T const& max(T const& a, T const& b) { template <typename T> inline T const& max(T const& a, T const& b, T const& c) {
inline char const* max(char const* a, char const* b) {
Now, with this call ::max(s1, s2, s3); we won't have the warning but, look out, because at this form we aren't using return (std::strcmp(a, b) < 0) ? b : a; .
For the moment, as a rule of thumb you always have all
overloaded versions of a function declared before the function is called.
Labels:
Overloading
Friday, February 17, 2012
Pointers and Constants
Hello everybody,
This is a fast example to give expression to pointer and constants.
I had some questions about char const* const, so now, I try to explain what exactly means.
If you continue having questions don't doubt to ask me.
This is a fast example to give expression to pointer and constants.
I had some questions about char const* const, so now, I try to explain what exactly means.
If you continue having questions don't doubt to ask me.
#include <iostream> int main(){ char const* s1 = "Testing";//pointer to const variable char* const s2 = "constant";//const pointer char const* const s3 = "aloha";//const pointer and variable s1 = "testing 2";//works perfectly, reassigning the pointer to another const variable //*(s1+1) = 'A'; error, tring to change Testing to TAsting //*s1 is const, so can't be modify //s2 = "new string"; error, read-only, you can't change the pointer because is constant *s2 = 'A';//you can modify the value //s3 = "fuuuu"; error, constant pointer //*s3 = 'B'; error, constant value return 0; }
Thanks you all!
Thursday, February 16, 2012
Overloading Function Templates
Hello everybody
So far, we should have a bit idea of templates programming. In this post I'll speak about overloading functions center of attention to templates.
For a change, I follow using max function.
#include <iostream> namespace mySpace{ inline const int& max (const int& a, const int& b){ std::cout << "int"<< std::endl; return a>b?a:b; } template < class T > inline T max (const T& a, const T& b){ std::cout << "template"<< std::endl; return a>b?a:b; } template < class T > inline T max (const T& a, const T& b, const T& c){ std::cout << "template, 3: "<< std::endl; return max(max(a,b),c); } } int main(){ // calls the template with three arguments //and this one will call the non-template int function (by argument deduction) //because the better choise that compiler can make mySpace::max(2, 1, 9); //both types match perfectly, so calls non-template int (by argument deduction) mySpace::max(3, 5); // if the template can generate a better match, will be selected // there is neither a function with char arguments nor template, so // the call will be max<char> (by argument deduction) mySpace::max('a', 'b'); // calls max<double> (by argument deduction) mySpace::max(3.2, 5.4); // forcing the call template with no deduction // calls max<double> (no argument deduction) mySpace::max<double>(7, 42); // calls the nontemplate for two ints mySpace::max('a', 42.7); // calls max<int> (by argument deduction) // with <>, we are calling the template function // rather that a non-template exists mySpace::max<>(7, 42); return 0; }
Up to now it seems easy, what happends wheter we start using pointers and char types.
I like to be practical, so let's see another one:
#include <iostream> #include <cstring> #include <string> namespace mySpace{ template < class T > inline const T& max (const T& a, const T& b){ std::cout << "template"<< std::endl; return a>b?a:b; } template < class T > //I have changed the argument types to constants because I want to pass it by reference, (&) //note that const T& is equivalent to T const& inline T* const& max (T* const& a, T* const& b){ std::cout << "template pointer: "<< std::endl; return a>b?a:b; } //a fuction for C-string, char* /* * Note: Go back and remember the first template max; I didn't * show any call like max("abc","acd"), this one is deduced as * a char*, so, with the implementations we did (return a>b?a:b;), * this operacion is comparing the pointers and no his values * so this was a bug * * In order to resolve it, we may call max<std::string>("abc","acd") * or calling constructor like std::string: max(std::string("abc"),std::st * ring("acd") ) * * Or with the following function */ // inline char const* const& max (char const* const& a, char const* const& b){ std::cout << "c-string: "<< std::endl; return (std::strcmp(a,b) < 0) ? b : a; } } int main(){ int a = 5, b = 10; int* p_a = &a; int* p_b = &b; //pointers_to mySpace::max(a,b); //template mySpace::max(p_a,p_b); //template pointer mySpace::max(*p_a,*p_b); //template char const* s2 = "I'm so cool"; char const* s1 = "David"; mySpace::max(s1,s2); //c-string mySpace::max<>(s1,s2); //template, the output is David, WRONG! mySpace::max("asd","asdasd");//c-string std::string s3 = "David"; std::string s4 = "I'm so cool"; mySpace::max(s3,s4);//c-string //This is more typical in C char* s5 = "David";//warning: deprecated conversion from string constant to ‘char*’ char* s6 = "I'm so cool";//warning: deprecated conversion from string constant to ‘char*’ //warning: deprecated conversion from string constant to ‘char*’ //our function works with char const*, so call templates, //and how templates are constants this make a warning //but note that call can be wrong mySpace::max(s5,s6);//template return 0; }Note that I declared all templates as const and by reference, is a good practice to do unless you need to modify.
That's all today, next I will do a bit example with char const* using 3 parameters, like first exercice.
Labels:
Overloading
Monday, February 13, 2012
Argument Deduction
Hello everybody
Till now, we have been seeing the powerful of templates but, like I showed,
sometimes we can get some unexpected results so I like to check out this topic, argument deduction
There are so many times that C++ compiler must conclude that T must
be int or any type, what becomes this?
An example we unexpected:
Example:
std::cout << mySpace::max(15,21.1) << std::endl;
This call returns 21 (with warning), the comparison works perfectly but at the moment to return value, it's needed an int, so have to truncate the double. In fact, it's not as simple as truncate and the code instance a local variable (temporaly), making that our return definacion as constant is not exaclty correct, so makes a warning.
Therefore, it may sense to define a returning type, a new template, example:
In this way, we can control types and how RT can't be deduced we have to list explitly each template type..
Another approach is to specify only the return type, in general we must specify all the argument types up to the last argument that cannot be determined by deduction progress.
Thus, if we change the order of the templates we could only specify RT, let's see:
Till now, we have been seeing the powerful of templates but, like I showed,
sometimes we can get some unexpected results so I like to check out this topic, argument deduction
There are so many times that C++ compiler must conclude that T must
be int or any type, what becomes this?
An example we unexpected:
#include <iostream> #include <complex> namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented template < class T > // for any class/typename T inline const T& max (const T& a, const T& b){ // return a>b?a:b; // where the operator '>' is implemented } } int main(){ std::cout << mySpace::max(10,20) << std::endl;// works perfectly as we saw //std::cout << mySpace::max(15,21.1) << std::endl;// error: no matching function for call to ‘max(int, double)’ //std::cout << mySpace::max('a',21) << std::endl;// error: no matching function for call to ‘max(char, int)’ //some ways to fix it: // static_cast is a powerful tool, and I'm going to explain it while course std::cout << mySpace::max(static_cast<double>(15),21.1) << std::endl; // double(x) call the costructor of double, so 15 is an double std::cout << mySpace::max(double(15),21.1) << std::endl; // this is a bad practice, this cast is highlighting to the compiler that is a double //in this case works, but take care it std::cout << mySpace::max((double)15,21.1) << std::endl; //and the first solucion where we really use templates //we are forcing that max's call have to be doouble std::cout << mySpace::max<double>(15,21.1) << std::endl; //this examples also works but returning types is unexpected //we unlike 97's answer, we wanted a, as char //but our template function is unable to do it std::cout << mySpace::max<int> ('a',21) << std::endl; //This works too but don't make much sense std::cout << mySpace::max(int('a'),21) << std::endl; return 0; }This may appear to be a good method to enable passing two call parameters. Thus, we'll have two templates types as arguments.
Example:
#include <iostream> #include <complex> namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented template < class T1 , class T2 > // for any class/typename T inline const T1& max (const T1& a, const T2& b){ // return a>b?a:b; // where the operator '>' is implemented } } int main(){ std::cout << mySpace::max(10,20) << std::endl;//no problems //Now, the errors we had, have been fixed but isn't enough std::cout << mySpace::max(15,21.1) << std::endl; std::cout << mySpace::max('a',21) << std::endl; //both of us calls makes this warning //main.cpp:14:37: instantiated from here //main.cpp:6:22: warning: returning reference to temporary return 0; }This template method have some issues, for example, we're defining the return type with the first argument, then:
std::cout << mySpace::max(15,21.1) << std::endl;
This call returns 21 (with warning), the comparison works perfectly but at the moment to return value, it's needed an int, so have to truncate the double. In fact, it's not as simple as truncate and the code instance a local variable (temporaly), making that our return definacion as constant is not exaclty correct, so makes a warning.
Therefore, it may sense to define a returning type, a new template, example:
#include <iostream> #include <complex> namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented template < class T1, class T2, class RT > // for any class/typename T inline RT max (const T1& a, const T2& b){ //Note that return type isn't constant return a>b?a:b; // where the operator '>' is implemented } } int main(){ //correct but tedious std::cout << mySpace::max<int,double,double>(15,21.1) << std::endl; std::cout << mySpace::max<char,int,char>('a',100) << std::endl; return 0; }
In this way, we can control types and how RT can't be deduced we have to list explitly each template type..
Another approach is to specify only the return type, in general we must specify all the argument types up to the last argument that cannot be determined by deduction progress.
Thus, if we change the order of the templates we could only specify RT, let's see:
#include <iostream> #include <complex> namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented template < class RT, class T1, class T2 > // for any class/typename T inline RT max (const T1& a, const T2& b){ //Note that return type isn't constant return a>b?a:b; // where the operator '>' is implemented } } int main(){ std::cout << mySpace::max<double>(15,21.1) << std::endl; std::cout << mySpace::max<char>('a',100) << std::endl; std::cout << mySpace::max<char,char,int>('a',100) << std::endl; return 0; }
Labels:
Instantiation
Instantiation
Unfortunately, the tems instance and instantiate are used in a different context in object-oriented programming.
In general, when I speak about instantisate I will be refering at template context.
When we define a template, compiler review if the systax is correct but this isn't enough, for example, the example of max. We instantiated some calls to max with int, string, float. All examples worked well because each type had the operator > implemented, the review of this is what I call instantisate.
So, the instantiation is when compiler checks whether some calls like max(a,b) can make it. In the example you'll see below you can see when compiler make an error for instantisation because our template method is assuming we can use the operator > and isn't implemented.
Below an example where instantisate makes an error:
#include <iostream> #include <complex> namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented template < class T > // for any class/typename T inline const T& max (const T& a, const T& b){ // return a>b?a:b; // where the operator '>' is implemented } } int main(){ std::complex<float> a(10,10.1); std::complex<float> b(10.1,10); std::cout << mySpace::max(a,b) << std::endl; return 0; } main.cpp: In function ‘const T& mySpace::max(const T&, const T&) [with T = std::complexFixing this error:]’: main.cpp:13:34: instantiated from here main.cpp:6:22: error: no match for ‘operator>’ in ‘a > b’
#include <iostream> #include <complex> namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented template < class T > // for any class/typename T inline const T& max (const T& a, const T& b){ // return a>b?a:b; // where the operator '>' is implemented } template<class T> inline bool operator>(const std::complex<T>& a, const std::complex<T>& b) { if(a.imag() > b.imag()) return true; return false; } } int main(){ std::complex<float> a(10,10.1); std::complex<float> b(10.1,10); std::cout << mySpace::max(a,b) << std::endl; return 0; }This solucions is prioritizing 'i' (complex number), but we could do anything.
Next topic, Argument Deduction.
Thanks you all.
Labels:
Instantiation
Sunday, February 12, 2012
Why templates?
What is a template?
Template programming is a programming method where we don't indicate what type we are using so this allow to make generals methods or whatever.
For instance, a typical scenario where we can use templates are data structures, such a list or a binary list. We just have indicate what class we are using.
Undestanding its usefulness:
For example:
In this example we are adding an empty class to a vector (it's a bit stupid, I know), note that this class don't have constructor, C++ makes default.
We can see that vector class is implemented with templates so just putting our class we can use it. Think about it.
Other examples are override methods. C++ allow override methods, for example:
Running this returns:
max_int
20
max_float
10.2
This isn't the answer we expected. And why is this happening?
C++ try to find a function to call isMaxA where it takes float arguments but cannot so call isMaxA with int arguments. Transforming float to int ( by trucation ) so out function compares 10>10 and, indeed, the answer is no instead of 10.2>10.1
So with this fool example we can see weaknesses and, in fact, it's a bit stupid to declare two methods that are exacly the same.
Solucion (templates):
That's all today, I'll explain slowly soon why I have used words like inline, namespace, class, etc
Just get the idea
See you soon
Template programming is a programming method where we don't indicate what type we are using so this allow to make generals methods or whatever.
For instance, a typical scenario where we can use templates are data structures, such a list or a binary list. We just have indicate what class we are using.
Undestanding its usefulness:
For example:
#include <vector> class MyClass{};//empty int main(){ std::vector<MyClass> myVector; myVector.push_back(MyClass()); return 0; }
In this example we are adding an empty class to a vector (it's a bit stupid, I know), note that this class don't have constructor, C++ makes default.
We can see that vector class is implemented with templates so just putting our class we can use it. Think about it.
Other examples are override methods. C++ allow override methods, for example:
#include <vector> #include <iostream> int max (int a, int b){ std::cout << "max_int" << std::endl; return a>b?a:b; } float max (float a, float b){ std::cout << "max_float" << std::endl; return a>b?a:b; } int main(){ int a = 10, b = 20; float c = 10.1, d = 10.2; std::cout << max(a,b) << std::endl; std::cout << max(c,d) << std::endl; return 0; }
Running this returns:
max_int
20
max_float
10.2
So we can see which function have been called.
The next code show a bit how C++ works and some mistakes we can make if we don't know it:
#include <iostream> bool isMaxA (int a, int b){// if a is maxim return true(1) else return false(0) std::cout << "max_int" << std::endl; return a>b; } int main(){ float c = 10.2, d = 10.1; std::cout << isMaxA(c,d) << std::endl; return 0; } /* This program print this: max_int 0
*/
This isn't the answer we expected. And why is this happening?
C++ try to find a function to call isMaxA where it takes float arguments but cannot so call isMaxA with int arguments. Transforming float to int ( by trucation ) so out function compares 10>10 and, indeed, the answer is no instead of 10.2>10.1
So with this fool example we can see weaknesses and, in fact, it's a bit stupid to declare two methods that are exacly the same.
Solucion (templates):
#include <iostream> #include <cstring> namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented template < class T > // for any class/typename T inline const T& max (const T& a, const T& b){ // return a>b?a:b; // where the operator '>' is implemented } } int main(){ int a = 10, b=20; float c = 10.2, d = 10.1; std::string e = "a"; std::string f = "aa"; std::cout << mySpace::max(a,b) << std::endl; std::cout << mySpace::max(c,d) << std::endl; std::cout << mySpace::max(e,f) << std::endl; std::max(a,b); return 0; }
That's all today, I'll explain slowly soon why I have used words like inline, namespace, class, etc
Just get the idea
See you soon
Labels:
Introduction
Introduction of myself and the goal of this blog
Welcome everybody!
Why are you here and why you won't leave?
If you are a programmer who'd like to improve his skills programming, making better practices or you
just are interested in new ways to programming or even more powerful tools, you are at right site.
By now, this course/blog is oriented to the use of C++ templates but likely I will do something more like some articles of interest. I'm assuming that if you are here is because you know programming so I won't explain anything of compiles, IDE's or whatever.
Beside, I'll try to teach and explain all I do but if you have any doubt you can ask me. My form
to teach maybe is a bit particular because I think that read posts isn't enough whenever you have
to program, how a writer learn to write? reading? no, writing. Of course that read would be good
but again, not enough.
Something I like to teach (this is not an index, it's a brainstorm ideas):
Why templates?
Function Templates
Argument Deduction
Instantiation
Polymorphic Power of Templetes
Namespaces
and more!
About myself
I'm a studient of computer engineering at Universitat Pompeu Fabra ( Barcelona ) and I did this blog with the goal of improve my english and show the power of templates in C++.
Thus, if there is any part that is hard to understand feel free to advise me.
If you would like to deepen in any subject or talk about any specific just say it!
Why are you here and why you won't leave?
If you are a programmer who'd like to improve his skills programming, making better practices or you
just are interested in new ways to programming or even more powerful tools, you are at right site.
By now, this course/blog is oriented to the use of C++ templates but likely I will do something more like some articles of interest. I'm assuming that if you are here is because you know programming so I won't explain anything of compiles, IDE's or whatever.
Beside, I'll try to teach and explain all I do but if you have any doubt you can ask me. My form
to teach maybe is a bit particular because I think that read posts isn't enough whenever you have
to program, how a writer learn to write? reading? no, writing. Of course that read would be good
but again, not enough.
Something I like to teach (this is not an index, it's a brainstorm ideas):
Why templates?
Function Templates
Argument Deduction
Instantiation
Polymorphic Power of Templetes
Namespaces
and more!
About myself
I'm a studient of computer engineering at Universitat Pompeu Fabra ( Barcelona ) and I did this blog with the goal of improve my english and show the power of templates in C++.
Thus, if there is any part that is hard to understand feel free to advise me.
If you would like to deepen in any subject or talk about any specific just say it!
Subscribe to:
Posts (Atom)