First, for a SFINAE solution, the ellipsis overload trick (with expression SFINAE) still works, we just need to find a construct that is ill-formed when applied to a bitfield while well-formed when applied on a normal member.
For example, you cannot form a pointer-to-member if the member is a bitfield.
So we could write:
template <typename T>
struct is_bit_field_t {
template <typename U>
static constexpr bool helper(...) {
return true;
}
template <typename U>
static constexpr bool helper(decltype(&U::a) arg) {
return false;
}
static constexpr bool value = helper<T>(nullptr);
};
This will test whether the public member a
of U
is of bitfield type. Explanation:
- If
T::a
is a bitfield, then the expression decltype(&U::a)
would be ill-formed, which would then SFINAE out the second overload.
Live demo: https://godbolt.org/z/TTn8YPKW3
With C++20, this could be done with concepts, utilizing the fact that you cannot take the address of a bitfield.
The core implementation would just look like:
template <typename T>
concept A_IsBitField = !requires (T t) {
{ &t.a };
};
Live demo: https://godbolt.org/z/W3jbosY93
If you hate to define a standalone concept, you could just use the requires
expression instead (with the SFINAE method you have no choice but to create that struct)
Of course, the above only works for the member named a
. If you would like to use it with other members, then you need to rewrite the SFINAE trait or the concept, replacing a
with whatever member you want. You could write a macro to help you generate those constructs if you need a lot of them.
<type_traits>
doesn't have any tools mentioning bit fields, and since cppreference doesn't mention any SFINAE errors caused by bit fields, this probably isn't possible using templates. There might be some compiler built-ins that could help, but those wouldn't be portable. – Jubilate