Search code examples
c++memberenable-if

C++ enable_if is_member_function_pointer call depending on existence of member function


after having scrawled through several threads such as this one

SFINAE for detecting existence of non-member template function

without finding a solution, I am posting my question here. For a tiny logger class I want to surveil the amount of data logged using the << operator. When, for instance, a string is passe or an array having the .size() member function, I want to add size() number of bytes to that amount, sizeof(...) otherwise.

How can I (using VS 2010, which has very sparse C++11 support) make use of std::enable_if and std::is_member_function_pointer to reach my goal? How can I, for instance, define a function getSize() that operates as wanted?

The code below indicates what I want, but I guess I cannot define getSize twice...

Thanks a lot for help!

template<typename T,
typename = typename std::enable_if<std::is_member_function_pointer<decltype(&T::size)>::value> 
>
size_t getSize(const T &p_rObj)
{
    return p_rObj.size();
}

template<typename T,
    typename = typename std::enable_if<!std::is_member_function_pointer<decltype(&T::size)>::value>
>
size_t getSize(const T &p_rObj)
{
    return sizeof(p_rObj);
}

Addendumg/Edit: The following either does not work with VS 2010, which then expects every call of getSize(...) to have two parameters...

template<typename T> 
size_t getSize(std::enable_if<std::is_member_function_pointer<decltype(&T::size)>::value>, const T &p_rObj)
{
    return p_rObj.size();
}

template<typename T>
size_t getSize(std::enable_if<!std::is_member_function_pointer<decltype(&T::size)>::value>, const T &p_rObj)
{
    return sizeof(p_rObj);
}

Solution

  • You define twice

    template<typename T, typename> size_t getSize(const T &p_rObj);
    

    And you forget ::type and your traits is not SFINAE compatible

    The traits could be implemented:

    template <typename T> auto has_size_impl(int) -> decltype(std::declval<T>().size(), std::true_type{});
    template <typename T> auto has_size_impl(...) -> std::false_type;
    
    template <typename T>
    using has_size = decltype(has_size_impl<T>(0));
    

    You have to use std::enable_if in return type, template argument or regular argument:

    template<typename T>
    typename std::enable_if<has_size<T>::value, std::size_t>::type
    getSize(const T &p_rObj);
    

    or

    template<typename T, 
    typename std::enable_if<has_size<T>::value>::type* = nullptr>
    std::size_t getSize(const T &p_rObj);
    

    or

    template<typename T>
    std::size_t getSize(const T &p_rObj,
                        typename std::enable_if<has_size<T>::value>::type* = nullptr);
    

    Demo