Traits is a fun thing to do, and it's common in generic programming. One of the first papers out of the foreigner. Http://www.cantrip.org/traits.html?seenIEPage=1 recommends reading carefully.
First, let's look at a piece of code.
Template<class t>t accum (const t* ptr, int len) {T total = t (); for (int i = 0; i < len; i++) {Total + = * (ptr + i);} return total;}
This is a template function, very simple, is to add the parameters passed in, return the results.
So what's wrong with this piece of code?
Let's write a few lines of code to test:
int sz[] = {1, 2, 3, 4, 5};int v1 = Accum (&sz[0], 5)/5;char str[] = {' A ', ' B ', ' C ', ' d ', ' e '};char v2 = Accum (&s Tr[0], 5)/5;
The first two lines of code add 1,2,3,4,5 and then go to the average. The next two lines of code accumulate the ABCDE and then take the average.
The results of the operation are as follows:
V1 is right, V2 is wrong (right should be ' C '.
Then why is it right to pass in the int type, but is it wrong to pass in char?
Template<class t>t accum (const t* ptr, int len) {T total = t (); for (int i = 0, i < len; i++) {Total + = * (ptr + i);} return total;}
Let's take a look at this additive template function, note t total = t (), this line. When the const t* PTR points to an int array, T total is an int type. Then the 12345 Plus can be stored in the INT type variable.
However, when PTR points to a char array, total is the char type, and char has a length of only one byte of 8 bits. Except for the sign bit, when a char is used to hold a number, it is only 7 bits, 2 of 7 times = 128, that is, the maximum is 127. And ABCDE adds up to 495, and all crosses the boundary. When the additive function returns, it truncates the data. The result is a wrong average of-3.
OK, now we know that the key to this problem is that the data type of the storage result is too small.
What if we solve this problem?
There is an easy way
Method One: Add a template to specify the return value.
Since the key to the problem is that the return value type is too small, the first thing you can think of is specifying the return type. Like what:
Template<class T, Class Accut>accut accum2 (const t* ptr, int len) {accut total = Accut (); for (int i = 0; i < Len; i++) {Total + = * (ptr + i);} return total;}
Add a template parameter and then call the time to specify the return value type, such as:
Char v3 = Accum2<char, int> (&str[0], 5)/5;
This will give you the right results. This is really a solution, but not very good, the caller will have to specify the return value each time, it is troublesome and there is a mistaken situation. The next step is to introduce the traits solution.
Method Two: Traits
We can consider this question more generally, does it mean that each incoming parameter needs to have a corresponding type of storage result?
If each pass-in parameter has a corresponding return type, the problem should be solved.
We consider adding a template.
Template<typename t>struct traits;
This template doesn't have anything. Then use the template's specificity,
template<>struct traits<char>{typedef int accut;};
A special for Char.
To modify the additive function:
Template<class t>typename traits<t>::accut accum3 (const t* ptr, int len) {traits<t>::accut total = Traits<t>::accut (); for (int i = 0, i < len; i++) {Total + = * (ptr + i);} return total;}
Change the type of storage result to traits<t>::accut. Come and run at this time. Cumulative ABCDE:
Char v4 = ACCUM3 (&str[0], 5)/5;
Can get the right result ' C '.
Take a closer look at the accum3 and feel as if you get the return value type from inside the type T. It can also be understood that the return value type is an attribute of T. This is probably the source of the name traits.
What if we pass in an int array?
Char v4 = ACCUM3 (&sz[0], 5)/5;
The compiler directly error. That is because there is no traits of int. Adding a traits to int is possible:
template<>struct traits<int>{typedef int accut;};
What if we pass in a float array again? The compiler also gave an error because there was no float traits. This is also a benefit of traits, if the corresponding traits is not defined, then the compiler directly error, this can also find the problem earlier. Compared to the method, it does not require the caller to specify the return type themselves, reducing the likelihood of errors. Also, for example, if the int value added exceeds the maximum value of int, then modify int traits directly, for example, change to:
Template<>struct traits<int>{typedef long long accut;};
There is no need to change anywhere else.
Generally speaking, traits is still useful, if you encounter the need to specify a type of a corresponding type, you can often consider traits. The iterators inside the STL are also useful to traits.
Full code:
ConsoleApplication1.cpp:Defines the entry point for the console application.//#include "stdafx.h" #include <memory& GT, #include <Windows.h> #include <TlHelp32.h> #include <functional>template<class t>t accum ( Const t* PTR, int len) {T total = t (); for (int i = 0, i < len; i++) {Total + = * (ptr + i);} return total;} /***************************** append a return type **********************************************/template<class T, class Accut>accut accum2 (const t* ptr, int len) {accut total = Accut (); for (int i = 0; i < len; i++) {Total + = * (ptr + i);} return total;} /*****************************traits**********************************************/template<typename T> struct traits;template<>struct traits<char>{typedef int accut;}; template<>struct traits<int>{typedef int accut;}; Template<class t>typename traits<t>::accut accum3 (const t* ptr, int len) {traits<t>::accut total = Traits<t>::accut (); for (int i = 0; i <Len i++) {Total + = * (ptr + i);} return total;} int _tmain (int argc, _tchar* argv[]) {char tt = 128;int BB = Tt;int sz[] = {1, 2, 3, 4, 5};int v1 = Accum (&sz[0], 5)/ 5;char str[] = {' A ', ' B ', ' C ', ' d ', ' e '};char v2 = Accum (&str[0], 5)/5;char v3 = Accum2<char, int> (&str[0] , 5)/5;char v4 = accum3 (&str[0], 1)/1;return 0;}
C + + templates-traits