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?
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:
We can use this like
We know that
vector<int>andvector<double>are different types. But now we must also point out thatArray<int,137>andArray<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 likevector<double> x. When using template functions, most of the time the compiler can figure out the arguments. For example, to usestd::sort, you just say something likeHowever, you could also write
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:
Notice that the
parseFlagstemplate 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 likeHere, the 0 is a template argument (resolved at compile-time), and
myStringis 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_castthat can do conversions to and from string types. The function signature to convert from a non-string type to a string type isHere, if you call
lexical_cast, the compiler can figure out whatSourceis, but it can’t deduceTargetwithout some hints. To uselexical_cast, therefore, you’d write something likeMore 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
To call this function, you’d have to invoke it like this:
Here, this works because you have to explicitly tell the compiler what
IntArgumentis, but then the compiler can deduceTypeArgumentfrom the type of the argument toDoSomething.Hope this helps!