Search code examples
c++c++17template-meta-programmingtype-traitsincomplete-type

Use sizeof with incomplete type inside std::conditional


Here is a minimal example:

struct incomplete_type;

template<typename T>
struct foo
{
    using type = std::conditional_t<std::is_arithmetic_v<T>,
        std::conditional_t<sizeof(T) < sizeof(void*), int, float>,
        double>;
};

foo<incomplete_type> f; will cause error because it will do sizeof with type, even though incomplete_type is not a arithmetic type(iow, it will not go into sizeof branch logically). live demo

So, I want to defer sizeof:

first attempt(fail)

template<typename T>
auto
foo_aux()
{
    if(sizeof(T) < sizeof(T*))
        return 0;
    else
        return 0.0f;
}

conditional_t<std::is_arithmetic_v<T>, decltype(foo_aux<T>()), double> still trigger the same error.

second attempt(fail)

template<typename T, bool>
struct foo_aux_aux
{
    using type = float;
};
template<typename T>
struct foo_aux_aux<T, true>
{
    using type = int;
};

template<typename T, bool = false>
struct foo_aux : foo_aux_aux<T, sizeof(T) < sizeof(void*)>
{};

conditional_t<std::is_arithmetic_v<T>, typename foo_aux<T>::type, double> still trigger the same error.

third attempt(success)

template<typename T, bool comp>
struct foo_aux_aux
{
    using type = float;
};
template<typename T>
struct foo_aux_aux<T, true>
{
    using type = int;
};

template<typename T, bool isArithmeticType>
struct foo_aux
{
    using type = double;
};

template<typename T>
struct foo_aux<T, true>
{
    using type = typename foo_aux_aux<T, sizeof(T) < sizeof(void*)>::type;
};

Yes, it works as expected, but its really tedious and ugly.

Do you have an elegant way here?


Solution

  • In C++17, you can use if constexpr to do type computation. Just wrap the type into a dummy container and use value computation, then retrieve the type via decltype.

    struct foo could be implemented like:

    template<class T>
    struct type_ {
        using type = T;
    };
    
    template<class T>
    struct foo {
        auto constexpr static type_impl() {
            if constexpr (std::is_arithmetic<T>{}) {
                if constexpr (sizeof(T) < sizeof(void*)) {
                    return type_<int>{};
                } else {
                    return type_<float>{};
                }
            } else {
                return type_<double>{};
            }
        }
    
        using type = typename decltype(type_impl())::type;
    };
    
    static_assert(std::is_same<foo<incomplete_type>::type, double>{});