Search code examples
c++type-traitsenable-if

enable_if and auto return type?


I want to use type_traits to overload by shared_ptr or not.

struct A {
    A(int i) : x(i) {}
    int x;
};

int main()
{
    A a{4};
    auto b = std::make_shared<A>(7);
    A& c = a;
    A* d = b.get();
    A* e = &a;

    std::cout << getX(a) << std::endl;
    std::cout << getX(b) << std::endl;
    std::cout << getX(c) << std::endl;
    std::cout << getX(d) << std::endl;
    std::cout << getX(e) << std::endl;

    return 0;
}

This is one solution but has the problem that the return type must be pre-defined.

template <typename T>
typename std::enable_if_t<!boost::has_dereference<T>::value, int> getX(T t)
{
    return t.x;
}

template <typename T>
typename std::enable_if_t<boost::has_dereference<T>::value, int> getX(T t)
{
    return t->x;
}

But using the method below, I can make the return type auto but it kind of looks clunky.

template <typename T, typename std::enable_if_t<!boost::has_dereference<T>::value>* = nullptr>
auto getX(T t)
{
    return t.x;
}

template <typename T, typename std::enable_if_t<boost::has_dereference<T>::value>* = nullptr>
auto getX(T t)
{
    return t->x;
}

Is there a method to use the first type and still get return type auto?

I do not want to specify the type of A::x and so auto return type would be best. The second type does that but feels a bit clunky.

Or is there a better way of doing this? Thanks.


Solution

  • If you just want to run a different line of code or two for different types then constexpr if lets you do that without having to use enable_if and allows you to use automatic return type deduction. You can simplify getX into

    template <typename T>
    auto getX(T t)
    {
        if constexpr (boost::has_dereference<T>::value)
            return t->x;
        else
            return t.x;
    }
    

    and this works because the path that is not executed is discarded so at compile time only the true path compiled.