Determining whether Container<T>()==Container<T>() can compile?

Hi there,

Code such as

  std::vector<A> veca, vecb;
  veca == vecb;

can only compile if A()==A() can compile, if you'll forgive me taking some
descriptive shortcuts.

I tried writing a template to determine if a type can be equality-compared
for the purpose of type-erasure:

http://thread.gmane.org/gmane.comp.lib.qt.devel/11120

Adding auto and trailing return type seems to be helpful:

http://thread.gmane.org/gmane.comp.lib.qt.devel/11120/focus=11157

Would the suggestion from Olivier Goffart be something that could be applied
to libcxx to make it possible to write such a template for its stl container
implementations?

Does anyone else have any other ideas for something that would work with
todays stl implementations?

Thanks,

Steve.

It can be done yourself, or am I misunderstanding your question?
Example:

#include

#include

#include <type_traits>

template<typename T, typename U>
struct IsEqComparable
{
private:
typedef struct { char dmy; } yes;
typedef struct { char dmy[2]; } no;

template<typename T2, typename U2>
static auto test(void*) → typename std::enable_if<std::is_same<
decltype(std::declval() == std::declval()),
decltype(std::declval() == std::declval())

::value, yes>::type;

template<typename T2, typename U2> static no test(…);
public:
static const bool value = sizeof(test<T, U>(nullptr)) == sizeof(yes);
};

struct bar {};

int main()
{
if (IsEqComparable<std::vector, std::vector>::value)
std::cout << “Yes!\n”;
else
std::cout << “No!\n”;
if (IsEqComparable<std::vector, bar>::value)
std::cout << “Yes!\n”;
else
std::cout << “No!\n”;
return 0;
}

Mailing List Email wrote:

It can be done yourself, or am I misunderstanding your question?
Example:

    if (IsEqComparable<std::vector<int>, std::vector<int>>::value)
        std::cout << "Yes!\n";
    else
        std::cout << "No!\n";

Thanks for the suggestion.

Your solution does not work for nested containers, however:

    if (IsEqComparable<std::vector<bar>, std::vector<bar> >::value)
        std::cout << "Yes!\n";
    else
        std::cout << "No!\n";

outputs "Yes!".

Does that make the problem more clear? Any further suggestions?

Thanks,

Steve

That is unfortunate. I am afraid that, at this moment, I do not know of a better solution. It would seem that decltype erroneously makes the overloaded well-formed while the idea was that it should not.
Perhaps someone else will have another suggestion.

Mailing List Email wrote:

That is unfortunate. I am afraid that, at this moment, I do not know of a

better solution. It would seem that decltype erroneously makes the

overloaded well-formed while the idea was that it should not.

Perhaps someone else will have another suggestion.

It appears there is no known solution. Boost hit the same problem:

http://stackoverflow.com/questions/16791391/determining-whether-containert-containert-can-compile

However, as I wrote before, the problem becomes solvable if trailing return types are used:

>> Adding auto and trailing return type seems to be helpful:

>>

>> http://thread.gmane.org/gmane.comp.lib.qt.devel/11120/focus=11157

So, I'm wondering if we can change libcxx to use trailing return types for operators like that? Would such a change have to go into the c++ standard itself?

Thanks,

Steve

Well, actually, having thought about it, the following might work:

template<typename T, typename U>
struct IsEqComparable
{
private:
typedef struct { char dmy; } yes;
typedef struct { char dmy[2]; } no;

template<typename T2, typename U2>
static auto test(void*) → typename std::enable_if<std::is_same<
decltype(*std::declval().begin() == *std::declval().begin()),
decltype(*std::declval().begin() == *std::declval().begin())

::value, yes>::type;

template<typename T2, typename U2> static no test(…);
public:
static const bool value = sizeof(test<T, U>(nullptr)) == sizeof(yes);
};

This works fine for comparing vectors and probably any other container that exposes iterators through begin.
You will have to detect the possible type of the types, though.

The problem essentially comes from that there exists an operator == that compares two vectors, hence decltype(a == b) would succeed, because decltype does not evaluate its arguments.

Instead, the problem happens inside the operator == when it tries to compare the elements.

Yes, I think that's right. Using the type of begin() is a bit of a hack because it's not really typesafe. I'd prefer the stdlib container implementations to provide the correct result instead with patches something like

diff --git a/include/vector b/include/vector
index e04c267..41bd687 100644
--- a/include/vector
+++ b/include/vector
@@ -3168,8 +3168,9 @@ struct _LIBCPP_TYPE_VIS hash<vector<bool, _Allocator> >

  template <class _Tp, class _Allocator>
  _LIBCPP_INLINE_VISIBILITY inline
-bool
+auto
  operator==(const vector<_Tp, _Allocator>& __x, const vector<_Tp, _Allocator>& __y)
+->decltype(std::declval<typename vector<_Tp, _Allocator>::value_type&>() == std::declval<typename vector<_Tp, _Allocator>::value_type&>())
  {
      const typename vector<_Tp, _Allocator>::size_type __sz = __x.size();
      return __sz == __y.size() && _VSTD::equal(__x.begin(), __x.end(), __y.begin());

I can't actually figure out how to use libcxx on linux, so the above is not tested.

Thanks,

Steve.

Stephen Kelly wrote:

I can't actually figure out how to use libcxx on linux, so the above is
not tested.

I hacked around a bit with libcxxabi and tested the patch. The testcode now
indeed gives the expected results for std::vector:

#include <iostream>
#include <vector>

template<typename T, typename U = bool>
struct HasEqualityComparison
{
  enum { value = false };
};

template<typename T>
struct HasEqualityComparison<T, decltype(std::declval<T&>() ==
std::declval<T&>())>
{
  enum { value = true };
};

struct A {

};

struct B {
  bool operator==(const B &other)
  {
    return true;
  }
};

int main()
{
  std::cout << HasEqualityComparison<A>::value << std::endl;
  std::cout << HasEqualityComparison<std::vector<A>>::value << std::endl;
  std::cout << HasEqualityComparison<B>::value << std::endl;
  std::cout << HasEqualityComparison<std::vector<B>>::value << std::endl;
  std::cout << HasEqualityComparison<std::vector<std::vector<A>>>::value <<
std::endl;
  std::cout << HasEqualityComparison<std::vector<std::vector<B>>>::value <<
std::endl;
  return 0;
}

$ ./a.out
0
0
1
1
0
1

All that remains is figuring out how to get the stl implementations updated
to use that for all containers, if possible.

Thanks,

Steve.