1
#include <vector>
#include <iostream>
#include <type_traits>

using namespace std;

template<typename Coll>
class has_push_back
{
    using coll_type = decay_t<Coll>;
    using True = char(&)[1];
    using False = char(&)[2];

    template<typename U, void(U::*)(const typename U::value_type&)>
    struct SFINAE {};

    template<typename T>
    static True Test(SFINAE<T, &T::push_back>*);

    template<typename T>
    static False Test(...);

public:
    enum { value = sizeof(Test<coll_type>(nullptr)) == sizeof(True) };
};

class MyColl : public vector<int> {};

int main()
{
    cout << has_push_back<vector<int>>::value << endl;
    cout << has_push_back<MyColl>::value << endl;
}

The program above will output:

1
0

It shows the template has_push_back doesn't work if the function push_back is inherited.

Is there a way to make it work even if it is inheried?

4 Answers 4

2
template<typename Coll>
struct has_push_back {
    template<
        typename T,
        typename = decltype(
            std::declval<T&>().push_back(std::declval<typename T::value_type>())
        )
    >
    static std::true_type Test(int);

    template<typename T>
    static std::false_type Test(long);

    using type = decltype(Test<Coll>(0));
    static constexpr bool value = type::value;
};

Online Demo

Sign up to request clarification or add additional context in comments.

Comments

2

Here's a solution using void_t, which is standard in C++17 and also comes with additional utilities such as is_detected_exact in the Library Fundamentals v2 TS, taking most of the work out of has_push_back:

template<typename... Ts>
using void_t = void;

template<typename T>
using push_back_test = decltype(std::declval<T>().push_back(std::declval<typename T::const_reference>()));

template<typename T, typename = void>
struct has_push_back : std::false_type {};

template<typename T>
struct has_push_back<T, void_t<push_back_test<T>>> : std::is_same<push_back_test<T>, void> {};

Or with future utilities:

template<typename T>
using push_back_test = decltype(std::declval<T>().push_back(std::declval<typename T::const_reference>()));

template<typename T>
using has_push_back = std::experimental::is_detected_exact<void, push_back_test, T>;

If you'd like to learn about void_t in detail, I suggest checking out Walter Brown's CppCon 2015 talks.

Comments

1

For the sake of completeness, I'd like to post another approach not previously mentioned.
This is based on functions' definitions and an alias declaration.
It follows a minimal, working example:

#include <vector>
#include <type_traits>
#include<utility>

using namespace std;

template<typename T, typename... U>
constexpr auto f(int)
-> std::conditional_t<false, decltype(std::declval<T>().push_back(std::declval<U>()...)), std::true_type>;

template<typename, typename...>
constexpr std::false_type f(char);

template<typename T, typename... U>
using has_push_back = decltype(f<T, U...>(0));

class MyColl : public vector<int> {};

int main() {
    static_assert(has_push_back<vector<int>, int>::value, "!");
    static_assert(has_push_back<MyColl, int>::value, "!");
}

Comments

1

According to this answer, your code could look like this:

#include <type_traits>

// Primary template with a static assertion
// for a meaningful error message
// if it ever gets instantiated.
// We could leave it undefined if we didn't care.

template<typename, typename T>
struct has_push_back {
    static_assert(
        std::integral_constant<T, false>::value,
        "Second template parameter needs to be of function type.");
};

// specialization that does the checking

template<typename C, typename Ret, typename... Args>
struct has_push_back<C, Ret(Args...)> {
private:
    template<typename T>
    static constexpr auto check(T*)
    -> typename
        std::is_same<
            decltype( std::declval<T>().push_back( std::declval<Args>()... ) ),
            Ret    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        >::type;  // attempt to call it and see if the return type is correct

    template<typename>
    static constexpr std::false_type check(...);

    typedef decltype(check<C>(0)) type;

public:
    static constexpr bool value = type::value;
};

All credits to @jork

You've probably used code from this answer but it isn't working with inherited functions.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.