0

I have an STL-style iterator on a template sparse matrix class I wrote, YaleStorage<D>. I have written an operator== for the matrix class which allows for two matrices to be compared for which D may differ.

My iterators are also templates -- so I can have const_iterator and iterator as specializations of iterator_T<RefType>, where RefType may be either const D& or D&.

I need these iterators to be comparable even when D differs. I don't particularly need constness to differ (a const_iterator does not need to be compared to an iterator). Here's how I wrote the operator!= and such:

template <typename D>
class YaleStorage {
  template <typename RefType>
  class iterator_T {
    template <typename E, typename ERefType = typename enable_if_else<std::is_const<RefType>::value, const E&, E&>::type>
    bool operator!=(const typename YaleStorage<E>::template stored_diagonal_iterator_T<ERefType>& rhs) const { /* some comparison code */ }
  };
  typedef iterator_T<D&>       iterator;
  typedef iterator_T<const D&> const_iterator;

  const_iterator cbegin() const { /* returns a const_iterator */ }
  const_iterator cend() const { /* returns the end const_iterator */ }
};

Note that I wrote my own enable_if_else, since I couldn't find a similar function in the STL. Here is that:

template <bool B, typename TA, typename TB>
struct enable_if_else {};
template <typename TA, typename TB>
struct enable_if_else<true, TA, TB> {  typedef TA type; };
template <typename TA, typename TB>
struct enable_if_else<false, TA, TB> { typedef TB type; };

Unfortunately, when I try to do a regular iterator comparison (where D=E), I get a whole raft of errors, such as:

compiling ../../../../ext/nmatrix/storage/yale.cpp
../../../../ext/nmatrix/storage/yale.cpp: In instantiation of ‘VALUE nm::yale_storage::each_stored_with_indices(VALUE) [with DType = unsigned char; VALUE = long unsigned int]’:
../../../../ext/nmatrix/storage/yale.cpp:1686:3:   required from here
../../../../ext/nmatrix/storage/yale.cpp:1567:83: error: no match for ‘operator!=’ in ‘d != nm::YaleStorage<D>::cend() const [with D = unsigned char; nm::YaleStorage<D>::const_iterator = nm::YaleStorage<unsigned char>::iterator_T<const unsigned char&>]()’
../../../../ext/nmatrix/storage/yale.cpp:1567:83: note: candidates are:
In file included from ../../../../ext/nmatrix/storage/yale.cpp:64:0:
../../../../ext/nmatrix/storage/yale.h:438:10: note: template<class E, class ERefType> bool nm::YaleStorage<D>::iterator_T::operator!=(const typename nm::YaleStorage<E>::iterator_T<ERefType>&) const [with E = E; ERefType = ERefType; RefType = const unsigned char&; D = unsigned char]
../../../../ext/nmatrix/storage/yale.h:438:10: note:   template argument deduction/substitution failed:
../../../../ext/nmatrix/storage/yale.cpp:1567:83: note:   couldn't deduce template parameter ‘E’

I've tried explicitly casting the result of cend() so it knows what's on both sides of the comparison, but I can't see why it'd choke on this.

template <typename DType>
static VALUE each_with_indices(VALUE nm) {
  YaleStorage<DType> y(s);

  // The next line is 1567:
  for (typename YaleStorage<DType>::const_iterator d = y.cbegin(); d != y.cend()); ++d) {
    /* Do some stuff with d */
  }
}

I know how to explicitly specify template parameters for non-operator functions, but how do I do it for operators? Is there another, better approach? It seems to me that it should be obvious to the compiler what E is here.

Translunar
  • 3,739
  • 33
  • 55
  • 4
    `enable_if_else` sounds like a duplicate of C++11's [`std::conditional`](http://en.cppreference.com/w/cpp/types/conditional). – dyp Sep 05 '13 at 18:11
  • It looks like `E` is in a non-deduced context in your `operator!=` (non-deduced because you're using a nested type as the parameter type). – dyp Sep 05 '13 at 18:14
  • Can you use C++11 features? – dyp Sep 05 '13 at 18:15
  • Turning `iterator_T` into a non-nested type could help to simplify your issue. Is that possible in your case? – dyp Sep 05 '13 at 18:18
  • @DyP Oops! Looked everywhere for `std::conditional`. Thanks. I suppose I could make the iterators non-nested. When I learned to code 13 years ago, our teacher taught us to make them nested. But I'm not really sure why, honestly. – Translunar Sep 05 '13 at 18:22
  • As the implementation of the iterator depends on the implementation of the container, it's not a bad idea to make the nested. But you could as well put them in a namespace (not to pollute the namespace surrounding the container). Another solution would be to take any parameter for `operator!=` and inside the function `static_assert` via typedefs in the iterator class (if this is necessary at all). – dyp Sep 05 '13 at 18:38
  • You have an instance of *non-deduced context* here (there should be a lot of duplicate questions but I can't find any at the moment). – n. m. could be an AI Sep 05 '13 at 19:31
  • I *know* there have to be questions on this, but I haven't been able to find any either. =( – Translunar Sep 05 '13 at 20:01
  • Like [this](http://stackoverflow.com/questions/12640808/nested-template-and-parameter-deducing)? (searched for *deduce nested [c++]*) – dyp Sep 05 '13 at 21:40

0 Answers0