I would like to be able to check, at compile-time using static_assert
, that all variadic arguments to a class are created with the same size.
Here is the code I have come up with, but it doesn't work:
template<typename... Args>
class MyClass {
static size_t arg_reference_size;
constexpr static bool size_is_equal() {
return true;
}
template<typename First, typename... Others>
constexpr static bool size_is_equal() {
return arg_reference_size == sizeof(First) && size_is_equal<Others...>();
}
template<class First, typename... Others>
constexpr static bool check_args_size() {
arg_reference_size = sizeof(First);
return size_is_equal<Others...>();
}
static_assert(size_is_equal<Args...>(), "");
}
I'd move the type trait out of the class:
template<class T, class... R>
inline constexpr bool is_same_size_v = (... && (sizeof(T) == sizeof(R)));
template<typename... Args>
class MyClass {
static_assert(is_same_size_v<Args...>);
};
int main() {
MyClass<int, unsigned> x;
//MyClass<char, long double> y; // will very likely fail
}
If you'd like to keep the type trait internal to your class and build on your current idea, you could make sure that size_is_equal()
is declared to return a type with a static constexpr
variable set to true
or false
depending on the template parameters. You don't need an actual implementation of the function. The returned type will be enough. Here std::integral_constant<bool, true>
or std::integral_constant<bool, false>
will be returned - and they are different types with a static constexpr bool value
variable. You can then use decltype(<the returned type>)::value
to get to the actual result.
template<typename... Args>
class MyClass {
template<typename First, typename... Others>
static auto size_is_equal() ->
std::integral_constant<bool, (... && (sizeof(First) == sizeof(Others)))>;
static constexpr bool size_is_equal_v =
decltype(size_is_equal<Args...>())::value;
static_assert(size_is_equal_v, "nope");
};
A third option, if you really want your functions to have implementations and if you can't use C++17 fold expressions and need recursion instead:
#include <type_traits>
template <typename... Args>
class MyClass {
template <typename... Ts>
// SFINAE to only make this match if the param pack is empty:
constexpr static typename std::enable_if<sizeof...(Ts) == 0, bool>::type
size_is_equal() {
return true; // or `false` if an empty parameter pack should not be
// allowed
}
template <typename> // a single parameter to terminate recursion
constexpr static bool size_is_equal() {
return true; // a single parameter is always true
}
// Take at least two template parameters and pass one of them on to the
// recursive call:
template <typename First, typename Second, typename... Others>
constexpr static bool size_is_equal() {
return sizeof(First) == sizeof(Second) &&
size_is_equal<Second, Others...>();
}
static_assert(size_is_equal<Args...>(), "not equal size");
};