Search code examples
c++templatespointersc++11enable-if

Recursively dereference pointer


While trying to answer one question here, I found this question:

How to recursively dereference pointer (C++03)?

Adapted code from the answer is following:

template<typename T> T& dereference(T &v) { return v; }

template<typename T> const T& dereference(const T &v) { return v; }


template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type
dereference(T *v) {
     return dereference(*v);
}

However, in this test it is failing to dereference pointer-to-pointer into the value type:

template <typename T>
class A
{
public:
         bool compare(T a, T b){
                return dereference(a) < dereference(b);
        }


};

int main()
{

        int u = 10;
        int *v = &u;
        int **w = &v;

        int i = 5;
        int *j = &i;
        int **k = &j;

        A<int> a;
        A<int*> b;
        A<int**> c;


        std::cout << a.compare(i, u) << std::endl;
        std::cout << b.compare(j, v) << std::endl;


        // This fails - 5 < 10 == 0
        std::cout << **k << " < " <<  **w <<  " == " << c.compare(k, w) << std::endl;


        return 0;

}

Obviously, w and k are derefenced only one time, which causes operator< to be called on two pointers.

I can fix this by adding the following:

template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type
dereference(T **v) {
     return dereference(*v);
}

but then it will fail for int***.

Is there any way to make this recursively without adding levels manually?

Note This is just "theoretical" question.


Solution

  • This is possible with the use of a custom can_dereference trait:

    template <typename T>
    struct can_dereference_helper {
        template <typename U, typename = decltype(*std::declval<U>())>
        static std::true_type test(U);
        template <typename...U>
        static std::false_type test(U...);
        using type = decltype(test(std::declval<T>()));
    };
    
    template <typename T>
    struct can_dereference :
      can_dereference_helper<typename std::decay<T>::type>::type {};
    

    and some mutually-recursive functions with a bit'o tag dispatching:

    template <typename T>
    auto recursive_dereference(T&& t, std::false_type) ->
      decltype(std::forward<T>(t)) {
        return std::forward<T>(t);
    }
    
    template <typename T>
    auto recursive_dereference(T&& t) ->
      decltype(recursive_dereference(std::forward<T>(t), can_dereference<T>{}));
    
    template <typename T>
    auto recursive_dereference(T&& t, std::true_type) ->
      decltype(recursive_dereference(*std::forward<T>(t))) {
        return recursive_dereference(*std::forward<T>(t));
    }
    
    template <typename T>
    auto recursive_dereference(T&& t) ->
      decltype(recursive_dereference(std::forward<T>(t), can_dereference<T>{})) {
        return recursive_dereference(std::forward<T>(t), can_dereference<T>{});
    }
    

    See it work live at Coliru. This may seem like overkill compared to Kerrek's answer, but I went for a generic approach that will dereference anything that supports operator*. I'll let you decide which tool fits your problem best.