If we have a constructor that accepts 3 parameters of type bool:
struct BoolOnly {
BoolOnly (bool b1, bool b2, bool b3)
{}
};
The compiler validates that we are actually calling it with boolean arguments:
BoolOnly b1 {0,1,1}; //ok
BoolOnly b2 {0,1,4}; //error: narrowing conversion of ‘4’ from ‘int’ to ‘bool’
It gives a compiler error, if a values is outside the range for boolean, which is perfectly OK.
Now the problem: I want to have a variadic template constructor, which I want to accept only arguments of type bool. In other words, I want to be able to pass an initializer consisting only of 0s or 1s. And get a compiler error if I pass something else
struct BoolOnlyExactVariadic {
template<typename... Args>
BoolOnlyExactVariadic (Args... args)
{}
};
Here we cannot specify any type of the parameters. this constructor accepts any count of arguments of any types. There is still no support in C++ for homogeneous variadic template arguments.
The most advanced thing we have are concepts - they seem to be designed for setting restrictions on variadic template arguments. So, Let's try with std::same_as
template<typename T>
concept BooleanExact = std::same_as<T, bool>;
struct BoolOnlyExact {
template<typename... Args>
BoolOnlyExact (BooleanExact auto... b)
{}
};
But the problem is that this template does not even accept values that are in the bool range:
BoolOnlyExact t2 {0,0,0}; //note: the expression ‘(... && BooleanExact<auto:12>) [with auto:12 = {int, int, int}]’ evaluated to ‘false’
The arguments get converted to int, which is not exact conversion to bool. Let's try then with std::convertible_to:
template<typename T>
concept BooleanConv = std::convertible_to<T, bool>;
struct BoolOnlyConv {
template<typename... Args>
BoolOnlyConv (BooleanConv auto... b)
{}
};
Now the compilation succeeds, but there is no validation for argument values:
BoolOnlyConv t1 {4,4,5}; //ok, but we want to validate them
even if the values are outside the boolean range. (No idea why the compiler thinks that '5' is convertible to bool).
Is there any C++20/C++23 way to validate a variadic init list of bools at compile time, like the first example? Other than manually writing a function with 15 arguments? The problem here is that there may be cases with more arguments (for example, 9 or 25), for which separate functions will be needed?
I would forget the idea of passing 0
,1
as bools, use true
,false
, and check the type with std::same_as<bool>
.
The only other options I see are:
If all your bools are constexpr, and so is the constructor body, you can make the constructor consteval
and check the integers at compile-time to make sure they're in range.
Or perhaps create a template <bool...> struct BoolList {};
and pass that to the constructor. But that changes the call syntax.