2

Duplicate: Is it possible to write a template to check for a function's existence?

I'm implementing a template class, and I want to make sure the template is instantiated by a class that has operator<, operator==, etc. implemented. What is the way to do this?

Community
  • 1
  • 1
dazzphot
  • 307
  • 1
  • 3
  • 11
  • Operators overloading? – nbro Aug 23 '14 at 04:17
  • 3
    Just assume that it does? If it doesn't, then you get a compiler error. – T.C. Aug 23 '14 at 04:19
  • Pure virtual operator? – merlin2011 Aug 23 '14 at 04:26
  • Is this a duplicate of either http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence/264088#264088 or http://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature ? – jogojapan Aug 23 '14 at 05:28

3 Answers3

1

C++ templates have duck typing-like behavior. If it looks like a duck, walks like a duck and quacks like a duck, then it is a duck. Basically if you use an operator or call a function on your the instance of the template argument, that argument will be required to override that operator or implement that method.

Lance
  • 8,872
  • 2
  • 36
  • 47
1

As has been said already, if your C++ template class uses any function or member of the template parameter, the compiler will check during compilation that the instantiated type has those functions and members.

In case you need to check explicitly, you could use boost::concept_check, this library lets you specify constraints that the instantiated type needs to meet.

Example of the checking concept class:

template <class X>
struct InputIterator
  : Assignable<X>, EqualityComparable<X>
{
 private:
    typedef std::iterator_traits<X> t;
 public:
    typedef typename t::value_type value_type;
    typedef typename t::difference_type difference_type;
    typedef typename t::reference reference;
    typedef typename t::pointer pointer;
    typedef typename t::iterator_category iterator_category;

    BOOST_CONCEPT_ASSERT((SignedInteger<difference_type>));
    BOOST_CONCEPT_ASSERT((Convertible<iterator_category, std::input_iterator_tag>));

    BOOST_CONCEPT_USAGE(InputIterator)
    {
        X j(i);             // require copy construction
        same_type(*i++,v);  // require postincrement-dereference returning value_type
        X& x = ++j;         // require preincrement returning X&
    }

 private:
    X i;
    value_type v;

    // Type deduction will fail unless the arguments have the same type.
    template <typename T>
    void same_type(T const&, T const&);
};

Example of using that concept:

// In my library:
template <class It>
class generic_library_class
{
  BOOST_CONCEPT_ASSERT((InputIterator<It>));
  // ...
};

// In the user's code:  
class foo {
  //... 
};

int main() {
  generic_library_class<std::vector<char>::iterator> y;
  //... 
}
Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
NetVipeC
  • 4,402
  • 1
  • 17
  • 19
1

C++11 provides a very handy operator decltype(e) which allows one to query the resultant type of an expression which is not evaluated in runtime at all, only at the compilation time.

This, in conjunction with <type_traits>, can be used to hand-write the template helpers for static_assert's :

#include <type_traits>

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

template <typename T>
struct is_equality_comparable<T,
        typename std::enable_if<
          std::is_convertible<decltype(std::declval<T&>() == std::declval<T&>())
          , bool>{}>::type
        > : std::true_type {};

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

template <typename T>
struct is_less_than_comparable<T,
        typename std::enable_if<
          std::is_convertible<decltype(std::declval<T&>() < std::declval<T&>())
          , bool>{}>::type
        > : std::true_type {};

That is, you can put any expression within decltype(e) and check if it's e.g. convertible to boolean type, just like std::declval<T&>() < std::declval<T&>() for less-than-comparability.

Now, whenever you want to add constraints to your template's parameter, just put a static_assert within the class declaration.

Let's first define some classes that will be used for tests:

// sample equlity comparable type with **member** operator==
struct EqComparable
{
    bool operator==(const EqComparable& other)
    {
        return i == other.i;
    }

    int i;
};

// sample less-than comparable type with **global** operator<
struct LessThanComparable
{
    int i;
};

bool operator<(const LessThanComparable& lhs, const LessThanComparable& rhs)
{
    return lhs.i < rhs.i;
}

// sample class which is not comparable in any way
struct NotComparableAtAll
{
};

Now let's apply constraints to the classes:

// your template classes with constraints:

template <typename T>
class MustBeEqComparable
{
    static_assert(is_equality_comparable<T>::value,
                  "Type T is not equality comparable!");
};

template <typename T>
class MustBeLessThanComparable
{
    static_assert(is_less_than_comparable<T>::value,
                  "Type T is not less than comparable!");
};

Now let's try to instantiate each:

MustBeEqComparable<EqComparable> a;

MustBeEqComparable<NotComparableAtAll> b; // issues an error

MustBeLessThanComparable<LessThanComparable> c;

MustBeLessThanComparable<NotComparableAtAll> d; // issues an error

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160