I have a variadic function that can take any combination of input arguments, as long as each one of those arguments is convertible to bool
:
#include <concepts>
#include <cstddef>
// internal helper functions
namespace {
template <typename T>
constexpr std::size_t count_truths(T t) {
return (bool)t;
}
template <typename T, typename... Args>
constexpr std::size_t count_truths(T t, Args... args) { // recursive variadic function
return count_truths(t) + count_truths(args...);
}
}
template <typename T>
concept Booly = std::convertible_to<T, bool>;
// variadic function for which all arguments should be constrained to Booly<T>
// e.g. only_one(true, false, true, false, true) = false; only_one(true, false) = true
template <typename T, typename... Args> requires Booly<T>
constexpr bool only_one(T t, Args... args) {
return count_truths(t, args...) == 1;
}
I have attempted to constrain the templates using concepts to only allow bool-convertible types to be passed, but I have only managed to do so for the first parameter:
// following lines compile:
only_one(true, false, false);
only_one(BoolConvertible(), true, false);
// this line is correctly forced to failure due to the concept not being satisfied:
only_one(NonBoolConvertible(), false, true);
// BUT this line is not detected as a concept constraint failure (but still compilation failure):
only_one(true, NonBoolConvertible(), false, true);
How can I use C++20 concepts to constrain the remaining template parameters to ensure each one of them in Args...
satisfies Booly<>
?
You can simply use C++17 fold expression to do this
#include <concepts>
template<std::convertible_to<bool>... Args>
constexpr bool only_one(Args... args) {
return (bool(args) + ... + false) == 1;
}
static_assert(only_one(true, false, true, false, true) == false);
static_assert(only_one(true, false) == true);
static_assert(only_one() == false); // allow empty pack