I am used to angle brackets being used to specify a type, as a parameter:
vector<int> vecOfInts ;
But in rapidjson, there is code like this:
document.Parse<0>(json) ;
The document.Parse
method's signature is:
template <unsigned parseFlags>
GenericDocument& Parse(const Ch* str) {
RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
GenericStringStream<Encoding> s(str);
return ParseStream<parseFlags>(s);
}
I didn't know you could pass a value inside angle brackets - thought angle brackets were used for typenames alone.
What is the code here doing, and why is he passing a value in the angle brackets?
Is this a good idea? When?
An angle bracket or angle brace or angle cleat is an L-shaped fastener used to join two parts generally at a 90 degree angle. It is typically made of metal but it can also be made of wood or plastic. The metallic angle brackets feature holes in them for screws.
angular brackets are used for global use of the header files which are predefined and we include in our program. When you use angle brackets, the compiler searches for the file in the include path list. Angular brackets are used for standard inclusions.
Almost all functions require arguments enclosed in parentheses and separated by commas. If arguments are required, do not place any spaces between the function name and the left parenthesis.
One within the parentheses () and another within the angle brackets < > We know in the function call func(12) , argument 12 within the parentheses represents the arg parameter. Similarly in func<number>(12) , the argument number within the angle brackets represents the generic type parameter T .
There are two different factors going on here.
First, it's possible to define templates that are parameterized over things other than just types. For example, here's a simple array type:
template <typename T, size_t N> struct Array {
T arr[N];
};
We can use this like
Array<int, 137> myArray;
We know that vector<int>
and vector<double>
are different types. But now we must also point out that Array<int,137>
and Array<int,136>
are different types.
Second, when using templates, the compiler has to be able to figure out a value for all of the template arguments. When you're using template classes, this is why you typically specify all the template arguments. You don't say vector x
, for example, but instead say something like vector<double> x
. When using template functions, most of the time the compiler can figure out the arguments. For example, to use std::sort
, you just say something like
std::sort(v.begin(), v.end());
However, you could also write
std::sort<vector<int>::iterator>(v.begin(), v.end());
to be more explicit. But sometimes, you have a template function for which not all the arguments can be figured out. In your example, we have this:
template <unsigned parseFlags>
GenericDocument& Parse(const Ch* str) {
RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
GenericStringStream<Encoding> s(str);
return ParseStream<parseFlags>(s);
}
Notice that the parseFlags
template parameter can't be deduced from just the arguments of the function. As a result, to call the function, you must specify the template parameter, since otherwise the compiler can't figure it out. That's why you'd write something like
Parse<0>(myString);
Here, the 0 is a template argument (resolved at compile-time), and myString
is the actual argument (resolved at run-time).
You can actually have methods that combine a bit of type inference and a bit of explicit type parameters. For example, in Boost, there's a function lexical_cast
that can do conversions to and from string types. The function signature to convert from a non-string type to a string type is
template <typename Target, typename Source>
Target lexical_cast(const Source& arg);
Here, if you call lexical_cast
, the compiler can figure out what Source
is, but it can't deduce Target
without some hints. To use lexical_cast
, therefore, you'd write something like
std::string myString = boost::lexical_cast<std::string>(toConvertToString);
More generally, the compiler says that you have to specify some number of template arguments (optionally 0), and it will try to deduce the rest. If it can, great! If not, it's a compile-time error. Using this, if you'd like, you could write a function like
template <int IntArgument, typename TypeArgment>
void DoSomething(const TypeArgument& t) {
/* ... */
}
To call this function, you'd have to invoke it like this:
DoSomething<intArg>(otherArg);
Here, this works because you have to explicitly tell the compiler what IntArgument
is, but then the compiler can deduce TypeArgument
from the type of the argument to DoSomething
.
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With