I am writing a thin template wrapper for iterators, and hit a stumbling block when passing through the structure dereference operator, mainly because pointers don't have one:
#include <vector>
struct mystruct {
int member;
};
template<class iterator>
struct wrap {
typedef typename std::iterator_traits<iterator>::pointer pointer;
iterator internal;
pointer operator->() {return internal.operator->();} //MARK1
};
int main() {
wrap<std::vector<mystruct>::iterator> a;
a->member;
wrap<mystruct*> b;
b->member;
return 0;
}
http://ideone.com/XdvEz
prog.cpp: In member function ‘typename std::iterator_traits<_Iter>::pointer wrap<iterator>::operator->() [with iterator = mystruct*]’:
prog.cpp:18: instantiated from here
prog.cpp:11: error: request for member ‘operator->’ in ‘((wrap<mystruct*>*)this)->wrap<mystruct*>::internal’, which is of non-class type ‘mystruct*’
This following method works, but I don't think it's guaranteed to work. Namely, if an iterator has a strange pointer type that isn't the same as a pointer to a value_type.
pointer operator->() {return &*internal;} //MARK3
The standard indirectly says that an overloaded operator-> has to either return a pointer, an object that is convertible to a pointer, or an object that has overloaded operator->. Your best bet is to just return internal.
§13.5.6 [over.ref] p1
An expression x->m is interpreted as (x.operator->())->m
(The above applies recursively.)
Related
this is my code
#include <iostream>
#include <atomic>
using namespace std;
class T{
public:
int i = 0;
friend T operator++( T& t, int);
};
T operator++( T& t, int){
t.i++;
return T(); // please ignore this. I only care for it to compile right now
}
int main() {
atomic<T> t;
t++;
return 0;
}
I am trying to use atomic with a custom class B but im getting error:
*Compilation error #stdin compilation error #stdout 0s 4400KB
prog.cpp: In function ‘int main()’:
prog.cpp:21:3: error: cannot bind non-const lvalue reference of type ‘T&’ to an rvalue of type ‘T’
t++;
^~
In file included from prog.cpp:2:
/usr/include/c++/8/atomic:202:7: note: after user-defined conversion: ‘std::atomic<_Tp>::operator _Tp() const [with _Tp = T]’
operator _Tp() const noexcept
^~~~~~~~
prog.cpp:11:4: note: initializing argument 1 of ‘T operator++(T&, int)’
T operator++( T& t, int){
^~~~~~~~*
I am using friend to avoid using explicit conversion
(T(t))++;
if I am defining the operator++ with const like this:
friend T operator++(const T& t, int);
it compiles but then of course its useless to me.
When you do t++, it is the same as (t.operator T())++, which is equivalent to (t.load(std::memory_order_seq_cst))++.
This returns a copy of the value held by the atomic, which is an rvalue. Incrementing an rvalue doesn't make sense (It is destroyed immediately), so perhaps you want to lock, load and then store?
The std::atomic<T>::operator++ operators are only defined for integers and pointers (see the fine green print here).
The compiler attempts to invoke std::atomic<T>::operator T to obtain a temporary copy of the contained T instance, and then call your own operator ++ on it, which therefore requires a const reference parameter. atomic provides no way of locking, calling your own operator, and unlocking. Since this could lead to deadlocks (if your operator acquires some other lock), this would subvert atomic's purpose anyways.
You probably need to use a lock like std::mutex explicitly.
template<typename T> struct SomeClass{
void someFunc(const T& data) const {}
};
void testFunc(const int* a) {
SomeClass<int*> some_class;
some_class.someFunc( a);
}
I made a template instance with a non-const type. Now when calling a certain function I get errors that say:
error: invalid conversion from ‘const int*’ to ‘int*’
note: initializing argument 1 of ‘void SomeClass<T>::someFunc(const T&) const [with T = int*]’
So basically my const T& is treated as plain T&, the const is ignored. Why? How can I make sure in this case that it is seen by the compiler as const T&?
You may want to consider to partial specialize your class template SomeClass for the case T is a pointer. Then, add const to the type pointed to instead of the pointer itself (i.e., pointer to const instead of const pointer):
template<typename T> struct SomeClass<T*> {
void someFunc(const T* &data) const { /* ... */ }
};
SomeClass<int*>::someFunc() (i.e., T = int*) will be instantiated to:
void someFunc(const int* &data) const;
data above is a reference to a pointer to const int. However, with your primary template, SomeClass<int*>::someFunc() is actually:
void someFunc(int* const &data) const;
That is, data here is a reference to a const pointer to int. Therefore, you can't pass a, which is a const int*, as an argument to someFunc() since that pointed const int would be modifiable through the parameter data. In other words, the constness would be lost.
You need to change your definition to SomeClass<const int*> some_class;. The T comes from the definition and is int*, compiler is complaining rightfully.
The const is not ignored. It's applied to the type int*, yielding an int* that cannot be modified, i.e., int* const. In const int*, the const applies to the int, not to the pointer. That is, const int* points at an int that cannot be modified.
Inside testFunc you end up both consts. Since it's called with a const int*, the specialization of SomeClass has to be SomeClass<const int*>. And then when you call someFunc you get the second one; the actual argument type is const int* const. The first const applies to the int and the second const applies to the argument itself, i.e., to the pointer.
Assuming that the code base is huge and therefore you can't afford to write a specialization for your class template, you could provide the following delegating member template, someFunc(), which is an overload of your original member function:
#include <type_traits>
template<typename T> struct SomeClass {
// your original member function
void someFunc(const T &data) const { /* ... a lot of stuff ... */ }
// delegating member template
template<typename S>
void someFunc(const S* &data) const {
// delegate to original function
someFunc(const_cast<S*>(data));
}
};
First, this member template only comes into play with pointer arguments. Second, what it really does is to delegate the call to your original member function with the same name by casting out the const from the pointed type.
I hope it helps.
Why did invalid covariant return type error occur?
I am trying to implement a template base iterator and a derived iterator.
Code:
template <typename T>
class BaseClassA{
public:
virtual bool operator!=(const BaseClassA<T> & A) const {}
virtual BaseClassA<T> operator++(T){}
} ;
template <typename T>
class DerivedClassA: public BaseClassA<T>{
private:
T* p;
public:
DerivedClassA<T> operator++(T){
DerivedClassA<T> tmp(*this);
++p;
return tmp;
}
bool operator!=(const DerivedClassA<T> & A) const {
return (A.p != p);
}
} ;
template <typename T>
class BaseClassB{
private:
BaseClassA<T> beginIter;
BaseClassA<T> endIter;
public:
virtual BaseClassA<T> begin(void){}
virtual BaseClassA<T> end(void){}
} ;
template <typename T>
class DerivedClassB{
private:
DerivedClassA<T> beginIter;
DerivedClassA<T> endIter;
public:
DerivedClassA<T> begin(void){ return beginIter; }
DerivedClassA<T> end(void){ return endIter; }
} ;
int main(void){
DerivedClassB<int> B;
B.begin() != B.end();
++B.begin();
}
Compiler Error (g++)
test.cpp: In instantiation of 'class DerivedClassA<int>':
test.cpp:35:26: required from 'class DerivedClassB<int>'
test.cpp:43:24: required from here
test.cpp:12:27: error: invalid covariant return type for 'DerivedClassA<T> DerivedClassA<T>::operator++(T) [with T = int]'
DerivedClassA<T> operator++(T){
^
test.cpp:5:31: error: overriding 'BaseClassA<T> BaseClassA<T>::operator++(T) [with T = int]'
virtual BaseClassA<T> operator++(T){}
^
test.cpp: In function 'int main()':
test.cpp:45:5: error: no match for 'operator++' (operand type is 'DerivedClassA<int>')
++B.begin();
^
test.cpp:45:5: note: candidate is:
test.cpp:12:27: note: DerivedClassA<T> DerivedClassA<T>::operator++(T) [with T = int]
DerivedClassA<T> operator++(T){
^
test.cpp:12:27: note: candidate expects 1 argument, 0 provided
C++ built-in covariance applies to references and pointers in the C++ object model.
Now this is C++. So if you don't like what C++ provides, you can write your own object model.
In your case you have iterators. These iterators want to be value types (because that is what C++ wants in its libraries), and you want them to be polymorphic.
Polymorphic value types are not supported naively using the C++ object model.
Using code like this or adobe poly or boost type erasure or boost any_range you can create a ducktype polymorphism system that supports value-type polymorphism.
Now your BaseClassB<T>::begin and end returns an any_iterator<T>. Things that match the concept, including DerivedClassA<T>, can be stored and manipulated within it. BaseClassA<T> becomes obsolete, as a type erasing iterator does not require a virtual base class for polymorphism.
DerivedClassB<T> also returns an any_iterator<T>. If you want naked access to the real iterators of DerivedClassB<T> have a function called get_naked_range() that returns the naked iterators of DerivedClassB<T>, which can be used in contexts where you are absolutely certain the type is DerivedClassB<T>. If you do so, also mark begin and end as final.
Note that such type erasure has runtime costs, and iterating through it will be slower than "raw naked" iteration. This only matters if you are doing this at a pretty low level in a high performance context, don't let it scare you away.
C++ only directly supports covariant result type for raw pointers and raw references.
One reason is because with class types, a covariant result could need more space than the caller, knowing only a base class declaration, has set aside for that result.
The templating in the example is not relevant to this issue.
In other news:
You don't want a virtual operator== because you don't want runtime checking of the validity of an equality comparision, really you don't.
operator++() and operator++(int) are the only two valid signatures, so you can't meaningfully template the argument type.
I am building an STL list. I made a decorator class (MyList) that is a list of a special class (ProtectMe). I want all of the items of the list to be const. So here's what I made:
#include <list>
using namespace std;
class ProtectMe{
private:
int data_;
public:
ProtectMe(int data):data_(data){
}
int data() const{return data_;}
};
class MyList{
private:
//A list of constant pointers to constant ProtectMes.
list<const ProtectMe* const> guts_;
public:
void add(const ProtectMe& data){
guts_.push_front(&data);
}
};
I get the following compile error:
error: ‘const _Tp* __gnu_cxx::new_allocator::address(const _Tp&) const [with _Tp = const ProtectMe* const]’ cannot be overloaded
I'm still scratching my head trying to decode where I went wrong. Why doesn't this code compile? What should I change?
The value_type of standard containers must be CopyInsertable (or MoveInsertable) in order for the push_front to work. The value type of list<const ProtectMe* const> is constant, so it's not CopyInsertable.
† CopyInsertable means that
allocator_traits<A>::construct(m, p, v);
is well defined where p is a pointer to value_type, which by default calls placement new on p and thus requires it to be a non-const pointer.
The offending code:
template<typename T>
class SharedObject {
public:
typedef boost::intrusive_ptr<T> Pointer;
typedef boost::intrusive_ptr<T const> ConstPointer;
inline Pointer GetPointer() {
return Pointer(this); //Ambiguous call here
}
inline ConstPointer GetPointer() const {
return ConstPointer(this);
}
...
and used like this:
template <typename T>
class SomeClass: public SharedObject<SomeClass<T> > {
public:
static inline boost::intrusive_ptr<SomeClass<T> > Create() {
return (new SomeClass)->GetPointer();
}
};
int main()
{
auto v = SomeClass<int>::Create();
}
GCC (4.4.1) with boost 1.41 gives this error upon instatiating the first (non-const) version of GetPointer():
error: call of overloaded ‘intrusive_ptr SharedObject<SomeClass<int> >* const)’ is ambiguous
boost/smart_ptr/intrusive_ptr.hpp:118: note: candidates are: boost::intrusive_ptr<T>::intrusive_ptr(boost::intrusive_ptr<T>&&) [with T = SomeClass<int>] <near match>
boost/smart_ptr/intrusive_ptr.hpp:94: note: boost::intrusive_ptr<T>::intrusive_ptr(const boost::intrusive_ptr<T>&) [with T = SomeClass<int>] <near match>
boost/smart_ptr/intrusive_ptr.hpp:70: note: boost::intrusive_ptr<T>::intrusive_ptr(T*, bool) [with T = SomeClass<int>] <near match>
To my less than arcane skills in C++, I can't see why there is any ambiguity at all. The two canditates at lines 188 and 94 takes an existing intrusive_ptr rvalue reference, which SharedObject::this certainly is not. The final candidate however is a perfect match (the bool argument is optional).
Anyone care to enlighten me as to what the problem is?
EDIT+answer: I finally realized that in
inline Pointer GetPointer() {
return Pointer(this); //Ambiguous call here
}
this refers to SharedObject while the Pointer typedef is SomeClass. (Which is pretty much what Butterworth pointed out right away).
inline Pointer GetPointer() {
return Pointer(static_cast<C*>(this));
}
Since I know this to really be SomeClass, inheriting from SharedObject, a static_cast makes the template class go 'round.
When you say:
typedef boost::intrusive_ptr<T> Pointer;
you are declaring a type which is an intrusive pointer to an int (because T is an int at that point), when the template is instantiated in your code. Your SharedObject class is not an int, so you can't instantiate such an intrusive pointer using this.
Edit: OK, I misunderstood your code, I'll try again. At:
return Pointer(this); //Ambiguous call here
this is a SharedObject , as per the error messages, however the pointer is typedefed to a SomeClass I think.
Your code is incredibly hard to understand - whatever it is you are trying to do, there must be a simpler way. And you seem to be missing a virtual destructor (and maybe a virtual function) in the base class.