C++20 introduced many improvements like requires
, concept
s, constraints, modules
and much more - functionality you really miss in C++17.
How can a scenario having conditional constructors be implemented in C++17, that could look like the following C++20 example (using requires
)?
template <typename T> concept has_type = requires { typename T::type; };
template <typename T>
class someClass {
public:
using data_t = typename std::conditional_t<has_type<T>, T, std::type_identity<T> >::type;
constexpr someClass(T const& _a, T const& _b) requires std::is_arithmetic_v<T> : a{_a}, b{_b} {}
constexpr someClass(data_t const& _a, data_t const& _b,) requires has_type<T> : a{_a}, b{_b} {}
private:
const data_t a, b;
};
One constructor has to be used in case of T
is an arithmetic type (int
, float
, double
, ...).
Another constructor needs to catch the case of T
being a class/struct having a nested type
alias (e.g. struct x { using type=float; };
).
Using SFINAE
template <typename, typename = std::void_t<>>
struct HasTypeT : std::false_type {};
template <typename T>
struct HasTypeT<T, std::void_t<typename T::type>> : std::true_type {};
template <typename T>
struct type_identity {
using type = T;
};
template <typename T>
class someClass {
public:
using data_t = typename std::conditional_t<HasTypeT<T>::value, T, type_identity<T> >::type;
template <typename U = T, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
constexpr someClass(T const& _a, T const& _b) : a{_a}, b{_b} {}
template <typename U = T, typename = std::enable_if_t<HasTypeT<U>::value>>
constexpr someClass(typename U::type const& _a, typename U::type const& _b) : a{_a}, b{_b} {}
private:
const data_t a, b;
};