I'm looking at this source code
template<char... digits>
struct conv2bin;
template<char high, char... digits>
struct conv2bin<high, digits...> {
static_assert(high == '0' || high == '1', "no bin num!");
static int const value = (high - '0') * (1 << sizeof...(digits)) +
conv2bin<digits...>::value;
};
template<char high>
struct conv2bin<high> {
static_assert(high == '0' || high == '1', "no bin num!");
static int const value = (high - '0');
};
template<char... digits>
constexpr int operator "" _b() {
return conv2bin<digits...>::value;
}
int array[1010_b];
and I'm wondering if this is even valid C++.
template<char high, char... digits>
struct conv2bin<high, digits...> {
What is this? A template specialization which doesn't specialize?
And why does a struct declaration has code lines inside it like
struct conv2bin<high> {
static_assert(high == '0' || high == '1', "no bin num!");
static int const value = (high - '0');
};
I'm confused..
Your code shows three new C++11 features: variadic templates, user-defined literals and static assertions.
The general variadic class template specifies zero or more arguments, the specialized versions one or more, and exactly one, respectively.
// digits can be the empty set, so 0 or more arguments
template<char... digits>
struct conv2bin;
// digits can be the empty set, so 1 or more arguments
template<char high, char... digits>
struct conv2bin<high, digits...>
// fully specialized for 1 argument
template<char high>
struct conv2bin<high>
The full syntax of variadic templates is a bit quirky, Wikipedia has a decent article on it. It is especially useful for another C++11 feature: perfect forwarding of a variadic number of function arguments.
The exotic looking int operator "" _b()
defines a user-defined literal, which is a way to add your own units to your types and expression. It simply means that integers followed by _b
are tagged with a certain "unit". See this question for more details. One practical benefit would be to avoid future Mars lander crashes (where SI and imperial units were being mixed in their landing software, without the compiler being able to diagnose it).
The static_assert
does exactly what you think it does: it asserts its condition statically, i.e. at compile-time. When the assertion fails, compilation is halted. This is a great way to detect errors as soon as possible.
UPDATE
Specialization of variadic templates can be very surprising when you have partially overlapping ranges of arguments: the zero or more arguments version will only match an empty list in your example (in case you would have provided a definition for it).
#include <iostream>
template<int... Args>
struct Test
{
enum { value = 0 };
};
template<int I, int... Args>
struct Test<I, Args...>
{
enum { value = 2 };
};
template<int I>
struct Test<I>
{
enum { value = 1 };
};
int main()
{
std::cout << Test<>::value << "\n"; // matches zero or more version
std::cout << Test<0>::value << "\n"; // matches single argument version
std::cout << Test<0, 0>::value << "\n"; // matches one or more version, not the zero or more one!
}
Output on LiveWorkSpace.
This is of course an example of the general rule for partial template specialization which states that the most specialized version will be selected (one-or-more is more specialized than zero-or-more, since the latter can always be used where the former can, but not vice versa). But because variadic templates are often not so "visibly" different from each other, you should be extra careful with their partial specializations.
template<char... digits>
struct conv2bin;
This is a template forward declaration. It doesn't have to be fully defined, because if it is used in unsupported way, you'll catch the error sooner (the compilation will fail). This particular example is not going to cause the compilation to fail, since the specializations are covering all possible cases.
template<char high, char... digits>
struct conv2bin<high, digits...> {
static_assert(high == '0' || high == '1', "no bin num!");
static int const value = (high - '0') * (1 << sizeof...(digits)) +
conv2bin<digits...>::value;
};
This is a partial specialization, where one template value is set. The rest are just forwarded to the "lower tier level" of the template type. This template structure is fully defined, and contains a int member variable, which value depends on the 'high' value and the next template.
template<char high>
struct conv2bin<high> {
static_assert(high == '0' || high == '1', "no bin num!");
static int const value = (high - '0');
};
Again partial template specialization, defining the value when the template parameters contains only one parameter in it's list.
So, in total this is a template meta-programming using variadic templates.
The static asserts are there to limit the values template variables can take.
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