When we write a C + + class library, in order to hide the implementation, often only reluctantly discard the powerful features of the template. But if we only need a limited number of template implementations, and we don't allow the user to pass in other types, we can put the instantiated code in the CPP file. However, when we need to do template biasing for a particular type, the correct wording becomes more complex because the GCC compiler does not allow the partial declaration to be made directly in the class. Through a simple example of log2 function, this paper provides a general method for both partial and instantiation in CPP, and can use Static_assert to check the implementation of parameters at compile time.
Now suppose we have a tool class called "Math," and all of its operations are provided as public static functions. Now we're going to add a log2 function, and there are two versions of this function:
int log2 (float value); float log2 (float value);
We know that in C + + A function overload must have a different argument list, which is definitely not a good way to declare it. But we don't want to add the return type to the function name, and it's natural to think of using the template. This way we expect to use the following methods:
int resultint = math::log2<int> (. f); float resultfloat = math::log2<float> (. f);
(PS. This example is for illustration purposes only, and it is still necessary to design the model parameters according to the specifications of the project.) )
Here we first give the LOG2 function an incorrect implementation, so that our example can be compiled and run up.
The code in the header file Math.h is as follows:
class Math {public: <class _type> static _type log2 (float value) { return0; }};
In Main.cpp, add our test code:
intMain () {intResultInt = math::log2<int> ( +. f); floatResultfloat = math::log2<float> ( +. f); printf ("ResultInt =%d\n", ResultInt); printf ("resultfloat =%f\n", resultfloat); return 0;}
Compile and run and you can see that the ResultInt and resultfloat on the console output are all 0.
Now this library function can be used. But everyone can see all of our implementation code in MATH.H, and if we want to hide it, we need to find a way to put them in the CPP file.
We notice that the return value of the LOG2 function can only be int or float, and it is meaningless to return other custom types, so we don't have to let all the compilation units see the implementation of the function, we just need to define an implementation in the CPP and then display the loading template class after the implementation body.
Now create a math.cpp and move the implementation part of our code over.
" Math.h " <class _type>_type math::log2 (float value) { return 0int math::log2<int> (float Float math::log2<float> (float value);
Compiled and run, we still get the same results.
Placing implementations in the CPP not only provides a covert implementation, but also provides an added benefit of linking errors when the user is instantiated with the wrong type.
If we add a sentence to the main function:
std::string resultstring = math::log2<std::string> (. f);
Compile again and we'll see the linker error:
" public:static class Std::basic_string<char,struct Std::char_traits<char>,class Std::allocator<char > > __cdecl math::log2<class std::basic_string<char,struct std::char_traits<char>,class std:: allocator<char> > > (float)" ([email protected][email protected][email Protected]@[email Protected]@[email protected]@2 @@[emailprotected]@@[email protected]@[email protected][email protected]@[ Email protected]@[email protected]@2 @@[emailprotected]@[email protected])
(VS2013)
Comment out this sentence (which is also used below), and we begin to add the correct implementation for LOG2.
First visit http://www.musicdsp.org/showone.php?id=91 here is a very fast log2 algorithm (not very precise, of course), But we have a problem when we want to use the code directly: this algorithm returns int's function floorOfLn2 and the function returning float approxLn2 is a completely different implementation, but our template function has only one! To solve this problem, you need to write a partial implementation of the int and float types.
Here's one notable point: when we are writing template biasing in Visual Studio, we can directly place the partial declaration directly in the definition section of the class. However, this cannot be compiled in GCC. If we want our code to cross the platform, we should follow the standard notation and put the partial implementation outside the class. Our example implementation of the code is already in the CPP, so we can directly add the partial code in the CPP.
The modified math.cpp are as follows:
#include"Math.h"Template<class_type>_type math::log2 (floatvalue) { return 0;} Template<>intmath::log2<int> (floatvalue) {Assert (Value>0.); ASSERT (sizeof(value) = =sizeof(int)); ASSERT (sizeof(value) = =4); return(((*(int*) &value) &0x7f800000) >> at)-0x7f;} Template<>floatmath::log2<float> (floatvalue) {Assert (Value>0.); ASSERT (sizeof(value) = =sizeof(int)); ASSERT (sizeof(value) = =4); inti = (* (int*) &value); return(((i&0x7f800000) >> at)-0x7f) + (i&0x007fffff)/(float)0x800000;} Templateintmath::log2<int> (floatvalue); Templatefloatmath::log2<float> (floatValue);
Compile and run, we see the program has output the correct result:
ResultInt = 9
Resultfloat = 9.953125
In addition, we note that the general implementation of the template function is:
Template <class _type>_type math::log2 (float value) { return 0 ;}
is no longer in use, we can delete it without any impact at all.
Now a close-to-perfect log2 function has been made.
So where is the inadequacy of it? We looked back at math.h and found that the function's declaration was only a bare template <class _type> static _type log2 (float value). For the user to see only this header file, it is perfectly conceivable that the function also has a qualified return value. If he accidentally uses the wrong type, the IDE will only give a very unfriendly link error, so the user must be mad.
To provide a friendly error hint, consider using the new feature Static_assert introduced by C++11, which can help us detect the types of errors and find them at compile time.
We need to introduce some type_traits to determine whether the type is the same at compile time.
Defined as follows:
structTrueType {Static Const BOOLValue =true;};structFalsetype {Static Const BOOLValue =false;}; Template<class_a,class_b>structIssametype:falsetype {};template<class_a>structIssametype<_a, _a>: TrueType {};
Next we wrap the original log function, place it under the private modifier, and rename it to _log (don't forget to modify the symbol in the math.cpp at the same time).
To create an identical log function, all we have to do is write the Static_assert check template parameter type in this function, and we will call the true _log2 function if the type is correct.
The modified math.h are as follows:
structTrueType {Static Const BOOLValue =true;};structFalsetype {Static Const BOOLValue =false;}; Template<class_a,class_b>structIssametype:falsetype {};template<class_a>structIssametype<_a, _a>: TrueType {};classMath { Public: Template<class_type>Static_type log2 (floatvalue) {Static_assert (Issametype<_type,int>::value | | Issametype<_type,float>:: Value,"template argument must be int or float"); return_log2<_type>(value); }Private: Template<class_type>Static_type _log2 (floatvalue);};
Restore the statement of the incoming std::string type that was previously commented out and compile again, and we'll see that this is a compile error, and the content is what we filled out in Static_assert.
This article by Hassan Ya Qi Original, reproduced please indicate the source.
C + + Template programming: How to make a non-generic template function implement declaration and definition separation