Search code examples
c++17metaprogramming

C++17 alternative to C++20 "requires" keyword


C++20 introduced many improvements like requires, concepts, 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; };).


Solution

  • 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;
    };
    

    Demo