Search code examples
c++pointerstemplatesdecltype

decltype: Access static member of class using a pointer


I have a templated function f. I am passing it either a reference or a pointer to an object. That object is of struct S. I would like to know the size of S::my_tuple, which is a static member of that struct.

I can do that when I pass the object by reference with std::tuple_size<decltype(T::my_tuple)>::value.

How can I do this for a pointer? This currently fails, since my_tuple is not a member of S*

Play with the code.

#include <tuple>
#include <type_traits>
#include <iostream>

struct S {
  constexpr static auto my_tuple = std::make_tuple(1, 2, 3, 4);
};


template <typename T>
int f(const T object) {

  // fails if T is a pointer
  if (std::is_pointer<T>::value) {
    // error
    return std::tuple_size<decltype(T::my_tuple)>::value;
   }

    // works if T is a reference
    return std::tuple_size<decltype(T::my_tuple)>::value;
}

int main() {
    S my_struct;
    std::cout << f(my_struct); // 4, correct size of properties 
    S* my_ptr = new S;
    std::cout << f(my_ptr); // does not compile
}

EDIT:

Thanks for the support. Here is the solution


Solution

  • Just remove pointer with a type trait:

    decltype(std::remove_pointer_t<std::decay_t<T>>::my_tuple)
    

    In const T object, T is never a reference. std::decay_t<T> is needed if you use, for example, a universal reference (T&& object) instead.

    Also note that all branches of your code should compile when you use if. If you want to discard a branch based on std::is_pointer_v<T>, take a look at constexpr if:

    if constexpr (std::is_pointer_v<T>) {
        // ...
    } else {
        // this code can be invalid if T is a pointer
    }
    

    Something like

    if (std::is_pointer_v<T>)
        return std::tuple_size_v<decltype(std::remove_pointer_t<T>::my_tuple)>;
    else
        return std::tuple_size_v<decltype(T::my_tuple)>;
    

    will not work in general.

    In your particular example you can just always remove pointer, because if T is not a pointer type, std::remove_pointer_t<T> is T itself:

    template <typename T>
    std::size_t f(T&&) {
        using Tuple = decltype(std::remove_pointer_t<std::decay_t<T>>::my_tuple);
        return std::tuple_size_v<Tuple>;
    }