std::conditional_t
can be nested perpetually:
#include <cstdint>
#include <cstring>
#include <type_traits>
enum class data_types { single_bytes, four_byte_integrals, four_byte_floats };
template<data_types expected_type>
std::conditional_t<expected_type == data_types::single_bytes, uint8_t,
std::conditional_t<expected_type == data_types::four_byte_integrals, int32_t,
float>>
parse(const uint8_t * rawBytes)
{
using return_t = std::conditional_t<expected_type == data_types::single_bytes, uint8_t,
std::conditional_t<expected_type == data_types::four_byte_integrals, int32_t,
float>>;
constexpr auto length = sizeof(return_t);
return_t result;
std::memcpy(&result, rawBytes, length);
return result;
}
However, this leaves the last option as the default case, if all conditions fail. This may be undesirable.
std::enable_if
exists to prevent template instantiation if a condition is not fulfilled:
#include <cstdint>
#include <cstring>
#include <type_traits>
enum class data_types { single_bytes, four_byte_integrals, four_byte_floats };
template<data_types expected_type>
std::enable_if_t<expected_type == data_types::four_byte_floats , float>
parse(const uint8_t * rawBytes)
{
float result;
constexpr auto length = sizeof(float);
std::memcpy(&result, rawBytes, length);
return result;
}
But nesting std::enable_if
into std::conditional
fails always, even if an earlier branch would suffice (std::conditional
does not short-circuit):
#include <cstdint>
#include <cstring>
#include <type_traits>
enum class data_types { single_bytes, four_byte_integrals, four_byte_floats };
template<data_types expected_type>
std::conditional_t<expected_type == data_types::single_bytes, uint8_t,
std::conditional_t<expected_type == data_types::four_byte_integrals, int32_t,
std::enable_if_t<expected_type == data_types::four_byte_floats, float>>>
parse(const uint8_t * rawBytes)
{
using return_t = std::conditional_t<expected_type == data_types::single_bytes, uint8_t,
std::conditional_t<expected_type == data_types::four_byte_integrals, int32_t,
std::enable_if_t<expected_type == data_types::four_byte_floats, float>>>;
constexpr auto length = sizeof(return_t);
return_t result;
std::memcpy(&result, rawBytes, length);
return result;
}
emits "error: no type named 'type' in 'struct std::enable_if<false, float>'" for e.g. this use:
int main()
{
uint8_t rawBytes[]{ 1u, 2u, 3u, 4u };
auto x{ parse<data_types::single_bytes>(rawBytes) };
}
How can we define a dependent type that will fail if no option is valid, but only if no option is valid?
You might delay instantiation with std::type_identity
and use extra ::type
std::conditional_t<>::type
using type =
typename std::conditional_t<
expected_type == data_types::single_bytes, std::type_identity<uint8_t>,
std::conditional_t<expected_type == data_types::four_byte_integrals, std::type_identity<int32_t>,
std::enable_if<expected_type == data_types::four_byte_floats, float>>
// ^^^^ no _t here
>::type; // extra `::type` here, for the type inside conditional_t