Random access iterator: What am I missing? - c++

I have a simple array-based container and I want to make an iterator for it. My goal is to make std::sort work so I'm using random access iterator.
Iterator Class:
class MyIterator: public std::iterator<std::random_access_iterator_tag, T> {
friend class ArraySequence;
private:
T* pos;
MyIterator(T* pos);
public:
MyIterator(const MyIterator &it);
~MyIterator();
public:
typename MyIterator::reference operator*() const;
typename MyIterator::pointer operator->() const;
typename MyIterator::reference operator[](const typename MyIterator::difference_type& n) const;
MyIterator operator++(int);
MyIterator& operator++();
MyIterator operator--(int);
MyIterator& operator--();
MyIterator operator+(const typename MyIterator::difference_type& n) const;
MyIterator& operator+=(const typename MyIterator::difference_type& n);
MyIterator operator-(const typename MyIterator::difference_type& n) const;
MyIterator& operator-=(const typename MyIterator::difference_type& n);
bool operator!=(const MyIterator& it) const;
bool operator==(const MyIterator& it) const;
bool operator<(const MyIterator& it) const;
bool operator>(const MyIterator& it) const;
bool operator<=(const MyIterator& it) const;
bool operator>=(const MyIterator& it) const;
};
And it's methods:
template <typename T>
ArraySequence<T>::MyIterator::MyIterator(T* pos): pos(pos) {}
template <typename T>
ArraySequence<T>::MyIterator::MyIterator(const MyIterator& it): pos(it.pos) {}
template <typename T>
typename ArraySequence<T>::MyIterator::reference ArraySequence<T>::MyIterator::operator*() const {
return *pos;
}
template <typename T>
typename ArraySequence<T>::MyIterator::pointer ArraySequence<T>::MyIterator::operator->() const {
return pos;
}
template <typename T>
typename ArraySequence<T>::MyIterator::reference ArraySequence<T>::MyIterator::operator[](const typename MyIterator::difference_type& n) const {
return *(pos + n);
}
template <typename T>
typename ArraySequence<T>::MyIterator ArraySequence<T>::MyIterator::MyIterator::operator++(int) {
return MyIterator(pos++);
}
template <typename T>
typename ArraySequence<T>::MyIterator& ArraySequence<T>::MyIterator::MyIterator::operator++() {
++pos;
return *this;
}
template <typename T>
typename ArraySequence<T>::MyIterator ArraySequence<T>::MyIterator::MyIterator::operator--(int) {
return MyIterator(pos--);
}
template <typename T>
typename ArraySequence<T>::MyIterator& ArraySequence<T>::MyIterator::MyIterator::operator--() {
--pos;
return *this;
}
template <typename T>
typename ArraySequence<T>::MyIterator ArraySequence<T>::MyIterator::operator+(const typename MyIterator::difference_type& n) const {
return MyIterator(pos + n);
}
template <typename T>
typename ArraySequence<T>::MyIterator& ArraySequence<T>::MyIterator::operator+=(const typename MyIterator::difference_type& n) {
pos += n;
return *this;
}
template <typename T>
typename ArraySequence<T>::MyIterator& ArraySequence<T>::MyIterator::operator-=(const typename MyIterator::difference_type& n) {
pos -= n;
return *this;
}
template <typename T>
typename ArraySequence<T>::MyIterator ArraySequence<T>::MyIterator::operator-(const typename MyIterator::difference_type& n) const {
return MyIterator(pos - n);
}
template <typename T>
bool ArraySequence<T>::MyIterator::operator!=(const MyIterator& it) const {
return pos != it.pos;
}
template <typename T>
bool ArraySequence<T>::MyIterator::operator==(const MyIterator& it) const {
return pos == it.pos;
}
template <typename T>
bool ArraySequence<T>::MyIterator::operator<(const MyIterator& it) const {
return pos < it.pos;
}
template <typename T>
bool ArraySequence<T>::MyIterator::operator>(const MyIterator& it) const {
return pos > it.pos;
}
template <typename T>
bool ArraySequence<T>::MyIterator::operator<=(const MyIterator& it) const {
return pos <= it.pos;
}
template <typename T>
bool ArraySequence<T>::MyIterator::operator>=(const MyIterator& it) const {
return pos >= it.pos;
}
template <typename T>
ArraySequence<T>::MyIterator::~MyIterator() {}
When I try to run std::sort I get a lot of compiler errors such as "Invalid operands to binary expression ('ArraySequence::MyIterator' and 'ArraySequence::MyIterator')" on line "difference_type __len = __last - __first;". What am I missing here? What can be improved?

You forgot that you need to implement operator - for the iterators themselves. Random access iterator support it1 - it2 to get a distance between the two iterators. You have operator - for the difference_type, but not for the iterator type itself. You need to add an overload like:
template <typename T>
typename MyIterator::difference_type ArraySequence<T>::MyIterator::operator-(const typename ArraySequence<T>::MyIterator& n) const
{
return pos - n.pos;
}

Related

Why my UniqPtr objects are double in size compared to std::unique_ptr?

In real applications I should stick to the standard library facilities, for practice and understanding how those facilities work I should try to implement my own.
Here I've implemented a simulation of the smart pointer unique_ptr:
#include<iostream>
#include <memory>
template <typename T>
class DefDel
{
public:
template <typename U>
void operator()(U* p)const
{
std::cout << "freeing memory...\n";
delete p;
}
};
template <typename T>
class DefDel<T[]>
{
public:
template <typename U>
void operator()(U* p)const
{
std::cout << "freeing memory of an array of objects...\n";
delete[] p;
}
};
template <typename T, typename D = DefDel<T>>
class UniqPtr final
{
public:
UniqPtr(T* = nullptr, D = DefDel<T>{});
UniqPtr(UniqPtr const&) = delete;
UniqPtr(UniqPtr&&) noexcept;
UniqPtr& operator =(UniqPtr const&) = delete;
UniqPtr& operator =(UniqPtr&&) noexcept;
~UniqPtr();
T& operator*();
T const& operator*() const;
T* operator->();
T const* operator->() const;
operator bool() const;
private:
T* ptr_{nullptr};
D del_{};
};
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(T* p, D del) :
ptr_(p),
del_(del)
{}
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(UniqPtr&& rhs) noexcept :
ptr_(std::move(rhs.ptr_)),
del_(std::move(rhs.del_))
{
rhs.ptr_ = nullptr;
}
template <typename T, typename D>
UniqPtr<T, D>& UniqPtr<T, D>::operator = (UniqPtr&& rhs) noexcept
{
if(this != &rhs)
{
ptr_ = std::move(rhs.ptr_);
del_ = std::move(rhs.del_);
rhs.ptr_ = nullptr;
}
return *this;
}
template <typename T, typename D>
UniqPtr<T, D>::~UniqPtr()
{
del_(ptr_);
}
template <typename T, typename D>
T& UniqPtr<T, D>::operator*()
{
return *ptr_;
}
template <typename T, typename D>
T const& UniqPtr<T, D>::operator*() const
{
return *ptr_;
}
template <typename T, typename D>
T* UniqPtr<T, D>::operator->()
{
return ptr_;
}
template <typename T, typename D>
T const* UniqPtr<T, D>::operator->() const
{
return ptr_;
}
template <typename T, typename D>
UniqPtr<T, D>::operator bool() const
{
return ptr_;
}
// for array
template <typename T, typename D>
class UniqPtr<T[], D> final
{
public:
UniqPtr(T* = nullptr, D = DefDel<T[]>{});
UniqPtr(UniqPtr const&) = delete;
UniqPtr(UniqPtr&&) noexcept;
UniqPtr& operator =(UniqPtr const&) = delete;
UniqPtr& operator =(UniqPtr&&) noexcept;
~UniqPtr();
T& operator*();
T const& operator*() const;
T* operator->();
T const* operator->() const;
operator bool() const;
private:
T* ptr_{nullptr};
D del_{};
};
template <typename T, typename D>
UniqPtr<T[], D>::UniqPtr(T* p, D del) :
ptr_(p),
del_(del)
{}
template <typename T, typename D>
UniqPtr<T[], D>::UniqPtr(UniqPtr&& rhs) noexcept :
ptr_(std::move(rhs.ptr_)),
del_(std::move(rhs.del_))
{
rhs.ptr_ = nullptr;
}
template <typename T, typename D>
UniqPtr<T[], D>& UniqPtr<T[], D>::operator = (UniqPtr&& rhs) noexcept
{
if(this != &rhs)
{
ptr_ = std::move(rhs.ptr_);
del_ = std::move(rhs.del_);
rhs.ptr_ = nullptr;
}
return *this;
}
template <typename T, typename D>
UniqPtr<T[], D>::~UniqPtr()
{
del_(ptr_);
}
template <typename T, typename D>
T& UniqPtr<T[], D>::operator*()
{
return *ptr_;
}
template <typename T, typename D>
T const& UniqPtr<T[], D>::operator*() const
{
return *ptr_;
}
template <typename T, typename D>
T* UniqPtr<T[], D>::operator->()
{
return ptr_;
}
template <typename T, typename D>
T const* UniqPtr<T[], D>::operator->() const
{
return ptr_;
}
template <typename T, typename D>
UniqPtr<T[], D>::operator bool() const
{
return ptr_;
}
int main()
{
UniqPtr<int[]> upi(new int[3]{57});
std::cout << sizeof(upi) << '\n';
std::unique_ptr<int[], DefDel<int[]>> upi2(new int[3]{57});
std::cout << sizeof(upi2) << '\n';
}
Why the size of my UniqPtr objects are double in size as std::unique_ptr (even being initialized with the same values)?
Is that because of my class is storing a Del_ object as a member?
If that is the problem then how could I achieve the very similar behavior as unique_ptr with 0 cost?
Why the size of my UniqPtr objects are double in size as std::unique_ptr (even being initialized with the same values)?
Is that because of my class is storing a Del_ object as a member?
Yes.
Because D del_ needs to have storage, and every T* ptr_ has to be properly aligned
If that is the problem then how could I achieve the very similar behavior as unique_ptr with 0 cost?
You can privately derive from D, rather than have it be a member. Then an instantiation with an empty class may cave it occupy no extra storage.
template <typename T, typename D = DefDel<T>>
class UniqPtr final : D
{
public:
UniqPtr(T* = nullptr, D = {});
UniqPtr(UniqPtr const&) = delete;
UniqPtr(UniqPtr&&) noexcept;
UniqPtr& operator =(UniqPtr const&) = delete;
UniqPtr& operator =(UniqPtr&&) noexcept;
~UniqPtr();
T& operator*();
T const& operator*() const;
T* operator->();
T const* operator->() const;
operator bool() const;
private:
T* ptr_{nullptr};
};
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(T* p, D del) :
D(del),
ptr_(p)
{}
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(UniqPtr&& rhs) noexcept :
D(std::move(*rhs)),
ptr_(std::exchange(rhs.ptr_, nullptr))
{}
template <typename T, typename D>
UniqPtr<T, D>& UniqPtr<T, D>::operator = (UniqPtr&& rhs) noexcept
{
using std::swap;
swap(static_cast<D&>(*this), static_cast<D&>(rhs));
swap(ptr_, rhs.ptr_);
}
template <typename T, typename D>
UniqPtr<T, D>::~UniqPtr()
{
static_cast<D&>(*this)(ptr_);
}

[C++]nested class shows me "does not name a type" error while making chainiterator

trying to make chained list iterator
headerfile(20112310_3.h)
#include<iostream>
template<typename T> class Chain;
template<typename T>
class ChainNode{
friend class Chain<T>;
private:
T data;
ChainNode<T> *link;
};
template<typename T>
class Chain{
public:
Chain();
class ChainIterator{
ChainIterator(ChainNode<T> startNode=0);
T& operator*() const;
T* operator->() const;
ChainIterator& operator++();
ChainIterator operator++(int);
bool operator!=(const ChainIterator right) const;
bool operator==(const ChainIterator right) const;
private:
ChainNode<T>* current;
};
private:
ChainNode<T>* first;
};
and here's cpp file(20112310_3.cpp)
#include<iostream>
#include "20112310_3.h"
using namespace std;
template<typename T>
Chain<T>::Chain(){first=0;}
template<typename T>
Chain<T>::ChainIterator::ChainIterator(ChainNode<T> startNode){
current=startNode;
}
template<typename T>
T& Chain<T>::ChainIterator::operator*() const{
return current->data;
}
template<typename T>
T* Chain<T>::ChainIterator::operator->() const{
return &current->data;
}
template<typename T>
ChainIterator& Chain<T>::ChainIterator::operator++(){
current=current->link;
return *this;
}
template<typename T>
ChainIterator Chain<T>::ChainIterator::operator++(int){
ChainIterator old=*this;
current=current->link;
return old;
}
template<typename T>
bool Chain<T>::ChainIterator::operator!=(const ChainIterator right) const{
return current!=right.current;
}
template<typename T>
bool Chain<T>::ChainIterator::operator==(const ChainIterator right) const{
return current==right.current;
}
int main(void)
{
return 0;
}
when i try to compile this it gets error
20112310_3.cpp:26:1: error: ‘ChainIterator’ does not name a type
ChainIterator& Chain::ChainIterator::operator++(){
20112310_3.cpp:32:1: error: ‘ChainIterator’ does not name a type
ChainIterator Chain::ChainIterator::operator++(int){
how could i resolve this?
template<typename T>
ChainIterator& Chain<T>::ChainIterator::operator++() {
ChainIterator is not in scope yet when it is parsed. You have two solutions -- either qualify the full type:
template<typename T>
typename Chain<T>::ChainIterator& Chain<T>::ChainIterator::operator++() {
... or use a trailing return type, at which point ChainIterator is in scope.
template<typename T>
auto Chain<T>::ChainIterator::operator++() -> ChainIterator& {

SFINAE : class member cannot be redeclared

I am trying to create my own "smart iterator" and I'd like to use SFINAE to have some operators depending on the tag of the iterator :
Here is my code :
template<class Iterator, class Predicat, class Tag>
class RangeFilterIterator {
public:
RangeFilterIterator(Iterator begin, Iterator end, Predicat predicat) :
mBegin(begin), mEnd(end), mPredicat(predicat) {}
bool operator !=(RangeFilterIterator const &r) {
return mBegin != r.mBegin;
}
typename Iterator::value_type &operator*() {return *mBegin;}
RangeFilterIterator &operator++() {
while(mBegin != mEnd && mPredicat(*mBegin++));
return *this;
}
template<class = std::enable_if_t<std::is_base_of<std::random_access_iterator_tag, Tag>::value>>
RangeFilterIterator &operator+(std::size_t n) {
while(n--)
++(*this);
return *this;
}
template<class = std::enable_if_t<!std::is_base_of<std::random_access_iterator_tag, Tag>::value>>
RangeFilterIterator &operator+(std::size_t n) = delete;
private:
Iterator mBegin, mEnd;
Predicat mPredicat;
};
template<typename Container, typename Predicate>
auto RangeFilter(Container const &c, Predicate p) {
using Iterator = RangeFilterIterator<typename Container::iterator,
Predicate,
typename Container::iterator::iterator_category>;
Iterator begin(const_cast<Container&>(c).begin(), const_cast<Container&>(c).end(), p);
Iterator end(const_cast<Container&>(c).end(), const_cast<Container&>(c).end(), p);
return Range(begin, end);
}
and at the line RangeFilterIterator &operator+(std::size_t n) = delete I got the error : class member cannot be redeclared.
I am not "good" with template, but I thought that with SFINAE only one of the two will be "declared". Am I missing something? It is possible to do otherwise?
Okay when I am using return type parameter instead of template parameter, it works.
template<class tag = Tag>
std::enable_if_t<std::is_base_of<std::random_access_iterator_tag, tag>::value, RangeFilterIterator>
&operator+(std::size_t n) {
while(n--)
++(*this);
return *this;
}
template<class tag = Tag>
std::enable_if_t<!std::is_base_of<std::random_access_iterator_tag, tag>::value, RangeFilterIterator>
&operator+(std::size_t n) = delete;

std::optional specialization for reference types

Why std::optional (std::experimental::optional in libc++ at the moment) does not have specialization for reference types (compared with boost::optional)?
I think it would be very useful option.
Is there some object with reference to maybe already existing object semantics in STL?
When n3406 (revision #2 of the proposal) was discussed, some committee members were uncomfortable with optional references. In n3527 (revision #3), the authors decided to make optional references an auxiliary proposal, to increase the chances of getting optional values approved and put into what became C++14. While optional didn't quite make it into C++14 for various other reasons, the committee did not reject optional references and is free to add optional references in the future should someone propose it.
The main problem with std::optional <T&> is — what should optRef = obj do in the following case:
optional<T&> optRef;
…;
T obj {…};
optRef = obj; // <-- here!
Variants:
Always rebind — (&optRef)->~optional(); new (&optRef) optional<T&>(obj).
Assign through — *optRef = obj (UB when !optRef before).
Bind if empty, assign through otherwise — if (optRef) {do1;} else {do2;}.
No assignment operator — compile-time error "trying to use a deleted operator".
Pros of every variant:
Always rebind (chosen by boost::optional and n1878):
Consistency between the cases when !optRef and optRef.has_value() — post-condition &*optRef == &obj is always met.
Consistency with usual optional<T> in the following aspect: for usual optional<T>, if T::operator= is defined to act as destroying and constructing (and some argue that it must be nothing more than optimization for destroying-and-constructing), opt = … de facto acts similarly like (&opt)->~optional(); new (&opt) optional<T&>(obj).
Assign through:
Consistency with pure T& in the following aspect: for pure T&, ref = … assigns through (not rebinds the ref).
Consistency with usual optional<T> in the following aspect: for usual optional<T>, when opt.has_value(), opt = … is required to assign through, not to destroy-and-construct (see template <class U> optional<T>& optional<T>::operator=(U&& v) in n3672 and on cppreference.com).
Consistency with usual optional<T> in the following aspect: both haveoperator= defined at least somehow.
Bind if empty, assign through otherwise — I see no real benefits, IMHO this variant arises only when proponents of #1 argue with proponents of #2, however formally it's even more consistent with the letter of requirements for template <class U> optional<T>& optional<T>::operator=(U&& v) (but not with the spirit, IMHO).
No assignment operator (chosen by n3406):
Consistency with pure T& in the following aspect: pure T& doesn't allow to rebind itself.
No ambiguous behavior.
See also:
Let’s Talk about std::optional<T&> and optional references.
Why Optional References Didn’t Make It In C++17.
There is indeed something that has reference to maybe existing object semantics. It is called a (const) pointer. A plain old non-owning pointer. There are three differences between references and pointers:
Pointers can be null, references can not. This is exactly the difference you want to circumvent with std::optional.
Pointers can be redirected to point to something else. Make it const, and that difference disappears as well.
References need not be dereferenced by -> or *. This is pure syntactic sugar and possible because of 1. And the pointer syntax (dereferencing and convertible to bool) is exactly what std::optional provides for accessing the value and testing its presence.
Update:
optional is a container for values. Like other containers (vector, for example) it is not designed to contain references. If you want an optional reference, use a pointer, or if you indeed need an interface with a similar syntax to std::optional, create a small (and trivial) wrapper for pointers.
Update2: As for the question why there is no such specialization: because the committee simply did opt it out. The rationale might be found somewhere in the papers. It possibly is because they considered pointers to be sufficient.
IMHO it is very okay to make std::optional<T&> available. However there is a subtle issue about templates. Template parameters can become tricky to deal with if there are references.
Just as the way we solved the problem of references in template parameters, we can use a std::reference_wrapper to circumvent the absence of std::optional<T&>. So now it becomes std::optional<std::reference_wrapper<T>>. However I recommend against this use because 1) it is way too verbose to both write the signature (trailing return type saves us a bit) and the use of it (we have to call std::reference_wrapper<T>::get() to get the real reference), and 2) most programmers have already been tortured by pointers so that it is like an instinctive reaction that when they receive a pointer they test first whether it is null so it is not quite much an issue now.
If I would hazard a guess, it would be because of this sentence in the specification of std::experimental::optional. (Section 5.2, p1)
A program that necessitates the instantiation of template optional
for a reference type, or for possibly cv-qualified types in_place_t or
nullopt_t is ill-formed.
I stumbled upon this several times and I finally decided to implement my solution that doesn't depend on boost. For reference types it disables assignment operator and doesn't allow for comparison of pointers or r-values. It is based on a similar work I did some time ago, and it uses nullptr instead of nullopt to signal absence of value. For this reason, the type is called nullable and compilation is disabled for pointer types (they have nullptr anyway). Please let me know if you find any obvious or any non-obvious problem with it.
#ifndef COMMON_NULLABLE_H
#define COMMON_NULLABLE_H
#pragma once
#include <cstddef>
#include <stdexcept>
#include <type_traits>
namespace COMMON_NAMESPACE
{
class bad_nullable_access : public std::runtime_error
{
public:
bad_nullable_access()
: std::runtime_error("nullable object doesn't have a value") { }
};
/**
* Alternative to std::optional that supports reference (but not pointer) types
*/
template <typename T, typename = std::enable_if_t<!std::is_pointer<T>::value>>
class nullable final
{
public:
nullable()
: m_hasValue(false), m_value{ } { }
nullable(T value)
: m_hasValue(true), m_value(std::move(value)) { }
nullable(std::nullptr_t)
: m_hasValue(false), m_value{ } { }
nullable(const nullable& value) = default;
nullable& operator=(const nullable& value) = default;
nullable& operator=(T value)
{
m_hasValue = true;
m_value = std::move(value);
return *this;
}
nullable& operator=(std::nullptr_t)
{
m_hasValue = false;
m_value = { };
return *this;
}
const T& value() const
{
if (!m_hasValue)
throw bad_nullable_access();
return m_value;
}
T& value()
{
if (!m_hasValue)
throw bad_nullable_access();
return m_value;
}
bool has_value() const { return m_hasValue; }
const T* operator->() const { return &m_value; }
T* operator->() { return &m_value; }
const T& operator*() const { return m_value; }
T& operator*() { return m_value; }
public:
template <typename T2>
friend bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs);
template <typename T2>
friend bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs);
template <typename T2>
friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
template <typename T2>
friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
template <typename T2>
friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
template <typename T2>
friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
template <typename T2>
friend bool operator==(const nullable<T2>& lhs, const T2& rhs);
template <typename T2>
friend bool operator==(const T2& lhs, const nullable<T2>& rhs);
template <typename T2>
friend bool operator==(const nullable<T2>& lhs, std::nullptr_t);
template <typename T2>
friend bool operator!=(const nullable<T2>& lhs, const T2& rhs);
template <typename T2>
friend bool operator!=(const T2& lhs, const nullable<T2>& rhs);
template <typename T2>
friend bool operator==(std::nullptr_t, const nullable<T2>& rhs);
template <typename T2>
friend bool operator!=(const nullable<T2>& lhs, std::nullptr_t);
template <typename T2>
friend bool operator!=(std::nullptr_t, const nullable<T2>& rhs);
private:
bool m_hasValue;
T m_value;
};
// Template spacialization for references
template <typename T>
class nullable<T&> final
{
public:
nullable()
: m_hasValue(false), m_value{ } { }
nullable(T& value)
: m_hasValue(true), m_value(&value) { }
nullable(std::nullptr_t)
: m_hasValue(false), m_value{ } { }
nullable(const nullable& value) = default;
nullable& operator=(const nullable& value) = default;
const T& value() const
{
if (!m_hasValue)
throw bad_nullable_access();
return *m_value;
}
T& value()
{
if (!m_hasValue)
throw bad_nullable_access();
return *m_value;
}
bool has_value() const { return m_hasValue; }
const T* operator->() const { return m_value; }
T* operator->() { return m_value; }
const T& operator*() const { return *m_value; }
T& operator*() { return *m_value; }
public:
template <typename T2>
friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
template <typename T2>
friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
template <typename T2>
friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
template <typename T2>
friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
template <typename T2>
friend bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs);
template <typename T2>
friend bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs);
template <typename T2>
friend bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);
template <typename T2>
friend bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);
template <typename T2>
friend bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);
template <typename T2>
friend bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);
template <typename T2>
friend bool operator==(const nullable<T2>& lhs, std::nullptr_t);
template <typename T2>
friend bool operator==(std::nullptr_t, const nullable<T2>& rhs);
template <typename T2>
friend bool operator!=(const nullable<T2>& lhs, std::nullptr_t);
template <typename T2>
friend bool operator!=(std::nullptr_t, const nullable<T2>& rhs);
private:
bool m_hasValue;
T* m_value;
};
template <typename T>
using nullableref = nullable<T&>;
template <typename T2>
bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs)
{
if (lhs.m_hasValue != rhs.m_hasValue)
return false;
if (lhs.m_hasValue)
return lhs.m_value == rhs.m_value;
else
return true;
}
template <typename T2>
bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs)
{
if (lhs.m_hasValue != rhs.m_hasValue)
return true;
if (lhs.m_hasValue)
return lhs.m_value != rhs.m_value;
else
return false;
}
template <typename T2>
bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
{
if (lhs.m_hasValue != rhs.m_hasValue)
return true;
if (lhs.m_hasValue)
return lhs.m_value != *rhs.m_value;
else
return false;
}
template <typename T2>
bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
{
if (lhs.m_hasValue != rhs.m_hasValue)
return true;
if (lhs.m_hasValue)
return lhs.m_value != *rhs.m_value;
else
return false;
}
template <typename T2>
bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
{
if (lhs.m_hasValue != rhs.m_hasValue)
return false;
if (lhs.m_hasValue)
return *lhs.m_value == rhs.m_value;
else
return true;
}
template <typename T2>
bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
{
if (lhs.m_hasValue != rhs.m_hasValue)
return true;
if (lhs.m_hasValue)
return *lhs.m_value != rhs.m_value;
else
return false;
}
template <typename T2>
bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
{
if (lhs.m_hasValue != rhs.m_hasValue)
return false;
if (lhs.m_hasValue)
return *lhs.m_value == *rhs.m_value;
else
return true;
}
template <typename T2>
bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
{
if (lhs.m_hasValue != rhs.m_hasValue)
return true;
if (lhs.m_hasValue)
return *lhs.m_value != *rhs.m_value;
else
return false;
}
template <typename T2>
bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
{
if (!lhs.m_hasValue)
return false;
return *lhs.m_value == rhs;
}
template <typename T2>
bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
{
if (!lhs.m_hasValue)
return true;
return *lhs.m_value != rhs;
}
template <typename T2>
bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
{
if (!rhs.m_hasValue)
return false;
return lhs == *rhs.m_value;
}
template <typename T2>
bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
{
if (!rhs.m_hasValue)
return true;
return lhs != *rhs.m_value;
}
template <typename T2>
bool operator==(const nullable<T2>& lhs, const T2& rhs)
{
if (!lhs.m_hasValue)
return false;
return lhs.m_value == rhs;
}
template <typename T2>
bool operator!=(const nullable<T2>& lhs, const T2& rhs)
{
if (!lhs.m_hasValue)
return true;
return lhs.m_value != rhs;
}
template <typename T2>
bool operator==(const T2& lhs, const nullable<T2>& rhs)
{
if (!rhs.m_hasValue)
return false;
return lhs == rhs.m_value;
}
template <typename T2>
bool operator!=(const T2& lhs, const nullable<T2>& rhs)
{
if (!rhs.m_hasValue)
return true;
return lhs != rhs.m_value;
}
template <typename T2>
bool operator==(const nullable<T2>& lhs, std::nullptr_t)
{
return !lhs.m_hasValue;
}
template <typename T2>
bool operator!=(const nullable<T2>& lhs, std::nullptr_t)
{
return lhs.m_hasValue;
}
template <typename T2>
bool operator==(std::nullptr_t, const nullable<T2>& rhs)
{
return !rhs.m_hasValue;
}
template <typename T2>
bool operator!=(std::nullptr_t, const nullable<T2>& rhs)
{
return rhs.m_hasValue;
}
}
#endif // COMMON_NULLABLE_H

Error compile "no match for operator==" [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
no matching function error using template template parameters in a function
Good mornig I don't understand because I obtain compile error by implementing as global function the operator== (in class myList::iterator)
If I realize the operator== as member function the code compile.
The error is
error: no match for operator== in it == it1
note: candidate is:
note: tempalte<class T> bool operator==(const typename myList<T>::iterator&,const typename myList<T>::iterator&)
The code is:
#include <iostream>
#include <cstdlib>
#include <iterator>
#include <typeinfo>
template <class T>
class myList;
template <class T>
bool operator==(const typename myList<T>::iterator& lhs,const typename myList<T>::iterator& rhs);
template <class T>
bool operator!=(const typename myList<T>::iterator& lhs,const typename myList<T>::iterator& rhs);
template <class T>
class myList
{
private:
class myInfo;
public:
//CTR DEFAULT
myList():_pInfo(NULL)
{}
myList(T value):_pInfo(new myInfo(value))
{}
// class iterator;
// friend class iterator;
class iterator{
public:
//creo gli iteratori
iterator():_pMyInfoIt(NULL)
{}
iterator(myInfo* p):_pMyInfoIt(p)
{}
iterator(const iterator& it):_pMyInfoIt(it._pMyInfoIt)
{}
iterator& operator=(const iterator& it)
{
_pMyInfoIt = it._pMyInfoIt;
return *this;
}
//creo funzioni che lavorano sugli iteratori
/*
bool operator==(const iterator& rhs)
{
return _pMyInfoIt == rhs._pMyInfoIt;
}
*/
friend bool operator== <T>(const typename myList::iterator& lhs,const typename myList::iterator& rhs);
friend bool operator!= <T>(const typename myList<T>::iterator& lhs,const typename myList<T>::iterator& rhs);
private:
myInfo* _pMyInfoIt;
};
myList::iterator begin()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return iterator(_pInfo);
}
private:
class myInfo
{
public:
myInfo(const T& data):_data(data),_pMyInfo(NULL)
{}
private:
T _data;
myInfo* _pMyInfo;
};
myInfo* _pInfo;
};
template <class T>
bool operator==(const typename myList<T>::iterator& lhs,const typename myList<T>::iterator& rhs)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return lhs._pMyInfoIt == rhs._pMyInfoIt;
}
template <class T>
bool operator!=(const typename myList<T>::iterator& lhs,const typename myList<T>::iterator& rhs)
{
return !(lhs == rhs);
}
int main(int argc,char** argv){
myList<int> test;
myList<int>::iterator it = test.begin();
myList<int>::iterator it1 = test.begin();
std::cout << typeid(it1).name() << std::endl;
if(it == it1)
std::cout << "EQUAL" << std::endl;
return EXIT_SUCCESS;
}
thanks in advance
Compiler can not deduce T. Change to:
template <class IT, class=typename IT::iterator_category>
bool operator==(const IT& lhs, const IT& rhs)
2nd template parameter does simple SFINAE, so that only iterators will match.
Define the friend functions within the class body:
friend bool operator== <>(const typename myList<T>::iterator& lhs, const typename myList<T>::iterator& rhs)
{
std::cout << __LINE__ << std::endl;
return lhs._pMyInfoIt == rhs._pMyInfoIt;
}
friend bool operator!= <>(const typename myList<T>::iterator& lhs, const typename myList<T>::iterator& rhs)
{
return !(lhs == rhs);
}
It should work.
As pimanych says, declare the body of the friend function inside the class body:
friend bool operator==(const iterator& lhs, const iterator& rhs)
{
return lhs._pMyInfoIt == rhs._pMyInfoIt;
}
friend bool operator!=(const iterator& lhs, const iterator& rhs)
{
return !(lhs == rhs);
}
Note that these aren't really template functions, but are only declared when myList<T> (and myList<T>::iterator) is instantiated. You can check by attempting to compare a myList<int>::iterator and myList<float>::iterator - you should get a compiler error.
Look up the Barton-Nackman trick for more details.