compile error in template member conversion operator - c++

I'm trying to write a conversion operator function template in a class and running into some compile errors which I don't fully understand.
class ABC { };
class BBC:public ABC { };
template <class T>
class TestPtr
{
public:
TestPtr(T* ptr=0)
: _pointee(ptr)
{ }
TestPtr(TestPtr& rhs)
{
this->_pointee = rhs._pointee;
rhs._pointee= 0;
}
template <class U> operator TestPtr<U>();
private:
T* _pointee;
};
template <class T> template <class U>
TestPtr<T>::operator TestPtr<U>()
{
return TestPtr<U>(this->_pointee); // if this line is changed to
//TestPtr<U> x(this->_pointee); // these commented lines, the
//return x; // compiler is happy
}
void foo (const TestPtr<ABC>& pmp)
{ }
int main() {
TestPtr<BBC> tbc(new BBC());
foo(tbc);
}
The above code results in the following errors
TestPtr.cpp: In member function ‘TestPtr<T>::operator TestPtr<U>() [with U = ABC, T = BBC]’:
TestPtr.cpp:38:9: instantiated from here
TestPtr.cpp:28:34: error: no matching function for call to ‘TestPtr<ABC>::TestPtr(TestPtr<ABC>)’
TestPtr.cpp:28:34: note: candidates are:
TestPtr.cpp:13:3: note: TestPtr<T>::TestPtr(TestPtr<T>&) [with T = ABC, TestPtr<T> = TestPtr<ABC>]
TestPtr.cpp:13:3: note: no known conversion for argument 1 from ‘TestPtr<ABC>’ to ‘TestPtr<ABC>&’
TestPtr.cpp:9:3: note: TestPtr<T>::TestPtr(T*) [with T = ABC]
TestPtr.cpp:9:3: note: no known conversion for argument 1 from ‘TestPtr<ABC>’ to ‘ABC*’
Now what is baffling to me is that the compiler is trying to pick TestPtr<ABC>::TestPtr(TestPtr<ABC>) instead of TestPtr<ABC>::TestPtr(ABC *) in the return statement. However if I create a variable with the intended constructor first and then return the value it works fine. I also made the T* constructor explicit with no avail.
I've tried with both g++ and clang++ with similar results. Can someone please explain what's going on here?

The problem is with your copy constructor. Its parameter is a TestPtr& (a non-const reference):
TestPtr(TestPtr& rhs)
A non-const reference cannot bind to an rvalue expression. TestPtr<U>(this->_pointee) is an rvalue expression that creates a temporary object. When you try to return this object directly, a copy must be made. Thus, you get this error when the compiler is unable to call the copy constructor.
Usually the solution would be to have your copy constructor take a const reference. However, in this case, the solution is a bit trickier; you'll want to do something similar to what std::auto_ptr does. I described its dirty tricks in an answer to "How could one implement std::auto_ptr's copy constructor?"
Alternatively, if you are using a recent C++ compiler and only need to support compilers that support rvalue references, you can make your class movable but noncopyable by providing an user-declared move constructor (TestPtr(TestPtr&&)) and by suppressing the copy constructor (declaring it =delete if your compiler supports deleted member functions, or declaring it private and not defining it).
Note also that you must provide a user-declared copy assignment operator. (Or, if you decide to make the type move-only, you'll need to provide a user-declared move assignment operator and suppress the implicit copy assignment operator, again using = delete or by declaring and not defining it.)

In your copy constructor you for some reason insist on zeroing-out the source pointer
TestPtr(TestPtr& rhs)
{
this->_pointee = rhs._pointee;
rhs._pointee= 0;
}
Why are you doing this?
Since you insist on zeroing-out rhs, you cannot declare the argument as const TestPtr& rhs, which in turn breaks your code, as James explained.
I don't see any reason to zero-out the source pointer. Just do
TestPtr(const TestPtr& rhs) : _pointee(rhs._pointee)
{}
and it should work.
I suspect that you used std::auto_ptr for inspiration and saw something like that in its copying routines. However, in std::auto_ptr the source pointer is zeroed-out because std::auto_ptr takes ownership of the object it points to and transfers that ownership when it gets copied. The need for the ownership management is dictated by the fact that std::auto_ptr not only points to objects, it also attempts to destroy them automatically.
Your pointer does not attempt to destroy anything. It doesn't need to take ownership of the pointed object. For this reason you don't need to zero-out the source when you copy your pointer.

Related

Warning: Multiple copy constructors defined while deleting them

Environment: VS2015 Update 3, 64 bit (debug|release) compile
In the code below, if I uncomment the Maybe(X&) = delete line, I get the warning mentioned in the code below and in the title of the question.
Now, I am aware, that there are certain rules in C++(11?), which might render
the explicit deletion of that constructor obsolete. Only, even after searching
the web for a while, I could not find a definite rule, which would confirm, that
if I only delete Maybe(const X&) = delete, the compiler will not auto generate the other copy constructor.
So my question is first and foremost:
Can anyone point me to spot in the C++ specification, which clearly defines the
rules for auto generation of copy constructors? Alternatively, some less official
easy to remember rule of thumb on how to be certain of what will happen would also be welcome.
template <class X>
class Maybe
{
X *m_just;
public:
explicit Maybe(const X& x)
: m_just(new X(x))
{}
Maybe()
: m_just(nullptr)
{}
Maybe(const Maybe<X>&& other)
: m_just(other.m_just)
{
other.m_just = nullptr;
}
Maybe(const Maybe<X>&) = delete;
// If line below is uncommented, this produces the warning:
// warning C4521: 'Maybe<Int32>': multiple copy constructors specified
// Maybe(Maybe<X>&) = delete;
~Maybe()
{
delete m_just;
m_just = nullptr;
}
// ... more members and code which are not related to question
// ...
};
Please do not comment on the whole idea of that class. It is just private tinkering in my private lab... ;)
The compiler will only generate one of Maybe(Maybe<X>&) and Maybe(const Maybe<X>&).
The conditions for the choice are listed in section 12.8, paragraph 8:
The implicitly-declared copy constructor for a class X will have the
form X::X(const X&) if
each direct or virtual base class B of X has a copy constructor whose first parameter is of type const B& or const volatile B&, and
for all the non-static data members of X that are of a class type M (or array thereof), each such class type has a copy constructor whose
first parameter is of type const M& or const volatile M&
Otherwise, the implicitly-declared copy constructor will have the form
X::X(X&)
Or more informally, the parameter will be const if and only if everything that needs to be copied can be "const-copied".
Deleting one will not cause the other to be generated.
Since you have no base class and your only member is a pointer, the generated constructor will be of the const variety and you can leave out the line causing an error.
My C++ is a little bit rusty so this might be wrong, but I don't think you should even have a non-const copy constructor (except for the move constructor naturally).
If a = b changes b you're going to surprise some people. Perhaps it's legitimate for the copy constructor to change the source in a way that's not externally visible, but in that case I think you're better off using const_cast inside the const copy constructor.
The error I don't think is about delete, it's about defining multiple copy constructors, which isn't allowed. I suspect even allowing the non-const copy constructor is a bit of a wart in the language.
other.m_just = nullptr;
This line should not compile. Something must be wrong with your compiler, or your code example doesn't exactly reflect what you're compiling.
Anyway, the move constructor should take a non-const rvalue reference.
The commented line is simply not necessary. Just leave it out. If you define the copy constructor (even as deleted), no other form of the copy constructor is defined by the compiler, ever, so if you leave the line out, there simply is no constructor that takes a non-const reference to Self, meaning that overload resolution will just pick the const reference copy constructor, which is deleted anyway.

Why do I get this error when I add another constructor?

I will gladly change the title of this thread to something more appropriate once I know what subject this falls under.
If I change the parameters of the error-causing constructor, there is no error.
This error only occurs if I include that exact constructor:
error: no matching function for call to 'Object::Object(Object)'
note: candidates are: Object::Object(Object&)
note: Object::Object()
Code:
#include <iostream>
using namespace std;
class Object {
public:
Object() {} /* default constructor - no problems at all */
Object( Object & toCopy ) {} /* Cause of error, but no error when commented out */
Object func() {
Object obj;
return obj;
}
};
int main() {
Object o = o.func(); /* this is the line that the error is actually on */
return 0;
}
You have declared an unusual and terrible copy constructor, namely one which takes the argument by non-constant reference. This is possible, and it means that no other copy constructor will be defined implicitly. However, it also means that you cannot construct copies from temporary values, since those don't bind to non-constant references.
Unless you had a reason to do what you were doing (maybe you want to define auto_obj :-) ?), you should declare the usual copy constructor with argument Object const & (but make sure to define it correctly!), or just leave it out entirely and rely on the implicitly generated version (as happens when you "comment out the line" in your code); or in C++11 you can default the copy constructor by declaring Object(Object const &) = default;.
(Note that the popular Rule of Three says that you either should not be defining your own, non-trivial copy constructor, or if you do, then you also need a user-defined destructor and assignment operator. Make sure your code is sane.)
the error you got
error: no matching function for call to 'Object::Object(Object)'
note: candidates are: Object::Object(Object&)
note: Object::Object()
means that compiler is looking for appropriate copy constructor for class Object. There is no such function in your class definition: your version cannot be used to bind temporary (see below). The correct version might be defined by yourself with:
Object(const Object& orig) {
// and initialize data here, and return
}
or you can just don't declare such function, and working version will be generated automatically, thus there is no error then as well.
note:
you are trying to define Object( Object & toCopy ) {}. This is not correct because taking non-const reference to temporary means you cannot use this temporary object to create a copy from it in your constructor because temporary objects cannot be assigned to non-const reference.
So to express this diferent way:
-> compiler is searching for copy constructor
-> finds your version
-> and this version tells: I will not do it
Finally:
Object o = o.func();
this is not good idea. Referring to nonstatic members of objects that have not been constructed or have already been destructed is undefined behavior. Make func() static so might be used like Object::func() or call it on properly constructed object.
Try Object(const Object &toCopy) { }
As already pointed out by many previously, the problem is that you only defined a copy constructor taking a reference, but the return of the call of o.func(); is an rvalue, which can only bind to rvalue references or const references, not to regular references.
Your code however has another issue, calling func() on o, which at that point is a yet uninitialized object, is a very bad idea. Maybe you intended to implement the factory idiom? In this case I would advise that you declare Object func(); static, and call it from the scope of Object, like so:
Object o = Object::func();

C++, Assignment to class instance from a function call?

I understand, or at least have an Idea of, why the following code does not work:
class Spambar {
public:
Spambar() {};
Spambar(Spambar& sb) {};
Spambar operator + (Spambar sb) {
Spambar new_sb;
return new_sb;
}
};
int main() {
Spambar sb1;
Spambar sb2;
Spambar sb3 = sb1 + sb2; // <<< Error: "No matching function for call to ... "
}
I guess, the problem is that the copy-constructor expects a reference to a Spambar instance. As no reference but a shallow instance is returned, the compilation fails.
So, how do I get that to work?
The problem is that the result of sb1 + sb2 is a temporary; the copy constructor used to initialise sb3 requires a non-const reference; and you can't take a non-const reference to a temporary.
You almost certainly want to fix this by changing the constructor's parameter type to Spambar const &. While you're at it, you should almost certainly do the same to operator+, and also make the operator itself const:
Spambar(Spambar const &);
Spambar operator + (Spambar const &) const;
If you're doing something very strange, and actually want the copy-constructor to modify its argument, then you'll have to either avoid passing temporaries to it, or do some nasty hackery with mutable or const_cast. In C++11, you would use a move constructor, with parameter type Spambar &&, for this sort of thing.
Your class does not have a copy constructor taking a const reference. Normally, a copy constructor looks like:
Spambar(const Spambar&);
The form you show is used in only very rare circumstances, and it is probably preventing your code from working.

non-static reference member ‘int& Property<int>::value’, can’t use default assignment operator

In some C++ code, I am encountering a compile-time error that seems like it might be indicating that I am trying to reseat a reference. But I feel quite sure that I am not trying to reseat a reference. I do not understand the cause of the error. Perhaps I am being silly, and missing something obvious. Or perhaps the problem reflects deep principles of C++ template programming that I do not adequately understand. Either way, I hope some of you can help. This is the code:
// p.cpp - slimmed down demonstration of error in prop.cpp
template<class T>
class Property {
protected:
T& value;
public:
explicit Property(T& a) : value(a) { }
// default "copy" setter
virtual Property<T>& operator=(T a) { value = a; return *this; }
// default "copy" getter
virtual operator T() const { return value; }
template<class U> // must invoke U virtual getter and T virtual setter
Property<T>& operator=(const Property<U>& newval)
{ return (*this = U(newval)); }
/* // uncommenting this eliminates the error
Property<T>& operator=(const Property<T>& newval)
{ return (*this = T(newval)); }
/**/
};
int main()
{
//* // this code produces the error
{
int i_ = 10, j_;
Property<int> i (i_), j (j_);
j = i;
}
/**/
/* // this code does NOT produce the error
{
int i_ = 10;
long j_;
Property<int> i (i_);
Property<long> j (j_);
j = i;
}
/**/
return 0;
}
When I compile this with gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 using the command g++ -o p p.cpp I get this output:
p.cpp: In member function ‘Property<int>& Property<int>::operator=(const Property<int>&)’:
p.cpp:4:7: error: non-static reference member ‘int& Property<int>::value’, can’t use default assignment operator
p.cpp: In function ‘int main()’:
p.cpp:33:13: note: synthesized method ‘Property<int>& Property<int>::operator=(const Property<int>&)’ first required here
Before you tell me that I am trying to reseat a reference, please keep in mind that:
Uncommenting the definition for Property::operator= that explicitly takes the same constructed type on the right side as the constructed type on the left side eliminates the error. So the problem is not, per se, that Property<int>::value is being accessed through *this.
Independently, as shown in main(), the operator= method template inside the Property class template is instantiated and works fine when the data type of the value on the right side of = is different from the data type on the left side of =.
value is a reference, but there is nothing wrong with assigning a value to it with =, at least in general. It is non-const. So once it is instantiated (which is guaranteed, via the initialization list in Property's only constructor), assigning a value to it does not attempt to reseat it, but instead assigns a new value to the memory location to which it refers.
I will provide the larger file prop.cpp which contains (successful) unit tests for each of Property's member functions, if requested.
You may notice that this code is an attempt to (partially) implement "function-like" ("C#-style") properties in C++. But this question is not about whether or not it would actually be appropriate to do this a real-world C++ project, nor is it about whether or not the pattern I have chosen is the most appropriate. It's possible that I'll ask separate questions about that later. If you want to comment on or criticize my approach, I am interested. I only ask that you not do it here, because it would distract from the more specific purpose of this question. Instead, you could post your own question, possibly with your own answer, and post a brief comment here linking to your new question.
As the compiler said, non-static reference cannot be assigned using the compiler-generated assignment operator. A user-defined copy assignment operator is required.
Your user-defined assignment operator performs assignment to the object referenced by value, by calling first your operator T() to obtain a temporary value of type T and then your Property<T>& operator=(T a) to execute value = a, which replaces the referenced-to object with the value obtained from the conversion operator.
If nothing is derived from your class, your operator= is equivalent to
// uncommenting this eliminates the error
Property<T>& operator=(const Property<T>& newval)
{
// return (*this = T(newval)); // convert to T, then assign
value = newval.value; // assign the referenced object
return *this;
}
The second case seems a little clearer: The user provided assignment operator is used.
{ U u = U(newval); // extract a value
*this = u; // use the custom assignment operator that takes a
// value as an argument and assigns to a reference
}
The first case is different: The user defined assignment operator is not picked and the compiler tries to synthesize it. This is not possible for non-static references and hence the program is ill-formed.

C++x0 unique_ptr GCC 4.4.4

I am trying to make use of the unique_ptr from C++x0, by doing
#include <memory>
and comping with -std=c++0x, however it is throwing up many errors this being an example.
/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../include/c++/4.4.4/bits/unique_ptr.h:214: error: deleted function ‘std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = Descriptor, _Tp_Deleter = std::default_delete<Descriptor>]’
UPDATE****
This is what I am trying to do, I have removed typedefs so you can see clearly the types
static std::unique_ptr<SomeType> GetSomeType()
{
std::unique_ptr<SomeType> st("Data","Data2",false);
std::unique_ptr<OtherType> ot("uniportantconstructor data");
st->Add(ot);
return st;
}
//Public method of SomeType
void Add(std::unique_ptr<OtherType> ot)
{
//otmap is std::map<std::string,std::unique_ptr<OtherType> >
//mappair is std::Pair<std::string,std::unique_ptr<OtherType> >
otMap.Insert(mappair(ot->name(),ot));
}
UPDATE:
If my class SomeType has a method that returns a element from the map (using the key) say
std::unique_ptr<OtherType> get_othertype(std::string name)
{
return otMap.find(name);
}
that would enure the caller would recieve a pointer to the one in the map rather than a copy?
std::unique_ptr<OtherType> ot("unimportant constructor data");
st->Add(ot);
You cannot pass an lvalue to a function accepting a unique_pointer, because unique_pointer has no copy constructor. You must either move the lvalue (to cast it to an xvalue) or pass a prvalue:
// pass an xvalue:
std::unique_ptr<OtherType> ot("unimportant constructor data");
st->Add(std::move(ot));
// note: ot is now empty
// pass a prvalue:
st->Add(std::unique_ptr<OtherType>("unimportant constructor data"));
Inside the Add method, things are a little more complicated. First, you have to move from ot, because formal parameters are always lvalues (since they have names). Second, you cannot move from ot and get ot->name() as arguments to the mappair function, because the order of argument evaluation is unspecified in C++. So we have to get ot->name() in a separate statement before moving from ot:
void Add(std::unique_ptr<OtherType> ot)
{
auto name = ot->name();
otMap.Insert(mappair(name, std::move(ot)));
}
Hope this helps. Note that under no (sane) circumstances can two unique_ptr objects point to the same thing. If you need that functionality, unique_ptr is not what you want.
It looks like you're trying to use the copy constructor. There isn't one. If your calling code looks like this:
T *ptr = /* whatever */;
std::unique_ptr<T> up = ptr;
You have to change the second line to this:
std::unique_ptr<T> up (ptr);
The original version is (basically) implicitly turning the assignment into:
std::unique_ptr<T> up (std::unique_ptr<T>(ptr));
The copy constructor has been deleted. "deleted function" is C++0x-speak for explicitly removing an implicit special member function. In this case, the copy constructor.
As #Steve said you're probably using the copy constructor, unique_ptr does not support copy semantics, if you want to move to value to another unique_ptr you're have to move it.
unique_ptr<T> other = std::move(orig); // orig is now null