I have a class with std::unique_ptr members which are initially initialized to nullptr and later assigned an actual object. They also use custom deleters.
I have two questions regarding this scenario: Should I use std::unique_ptr::reset() or copy assignment operator to assign a value to a unique pointer which was first initialized to nullptr? And if the latter is the case, then how can I make the code below work?
class MyClass {
private:
std::unique_ptr<Foo, void(*)(Foo*)> m_foo;
std::unique_ptr<Bar, void(*)(Bar*)> m_bar;
}
MyClass::MyClass() : m_foo(nullptr, ReleaseFoo), m_bar(nullptr, ReleaseBar) { }
bool MyClass::init()
{
m_foo.reset(CreateFoo()); // works ok
m_bar = std::unique_ptr<Bar, ReleaseBar>(CreateBar()); // 'ReleaseBar' is not a valid template type argument for parameter '_Dx'
}
Also, would the answer change if no custom deleters were involved (i.e. use reset with custom deleters and copy assignment otherwise)?
You can use resetor the move assignment operator to assign a new value.
The problem with your statement
m_bar = std::unique_ptr<Bar, ReleaseBar>(CreateBar()); // 'ReleaseBar' is not a valid template type argument for parameter '_Dx'
is, as the error message you quote indicates, that ReleaseBar is not a valid template argument. Earlier code uses ReleaseBar in a way consistent with this being a function name. In the earlier code the corresponding template argument was void(*)(Bar*).
Related
So if I have something like the following:
class MyClass{
public:
MyClass(){
// do other stuff
*oc = OtherClass(params);
}
private:
OtherClass* oc;
}
When is a constructor called on OtherClass? Would its default be called once as soon as the MyClass initialization begins, and then be redefined with its value constructor during the MyClass constructor? Or does it just not exist during "//do other stuff". What if no default constructor is provided for other class, only a value? Would it be good practice to construct it where it is defined as a member variable?
A default constructor is one that can be called without parameters. For example this is a default constructor:
struct foo {
foo(){} // (should not actually be user defined)
};
This is also a default constructor:
struct bar {
bar(int x = 42) {}
};
In your code it might be that the constructor that is called is a default constructor, but it does not matter for your code, because you do pass a parameter.
When is a constructor called on OtherClass?
In the line *oc = OtherClass(params);.
Would its default be called once as soon as the MyClass initialization begins, and then be redefined with its value constructor during the MyClass constructor?
If you do not provide an initializer members are default initialized. Confusingly for a pointer this means it is not initialized.
Or does it just not exist during "//do other stuff".
The member does exist before, but its value is indeterminate. You cannot use the value without invoking undefined behavior.
What if no default constructor is provided for other class, only a value?
See above. The existance of a default constructor of OtherClass is not relavant here. It would be relevant if the member was OtherClass and not a pointer, because for members of class type default initialization calls the default constructor.
Would it be good practice to construct it where it is defined as a member variable?
It is good practice to provide an initializer for members rather than assign in the constructor:
class MyClass{
public:
MyClass() : oc(params) {
}
private:
OtherClass oc;
}
I replaced the member with an instance rather than a pointer, because using a raw pointer as member opens up a can of worms that would require an even longer answer. For more on that read What is The Rule of Three?. Note that when the member is not a pointer but a OtherClass then suddenly it matters if OtherClass has a default constructor, because if you do not provide an initializer, then the member will be default constructed. Though in the above I used the member initializer list and the member will be initialized by the constructor that takes one parameter.
ÓtherClass *oc; is a pointer and as such has no constructor. It has to be initialized to a valid object before you can dereference it.
You can ensure oc is initialized by, well, initializing it:
MyClass() : oc(new OtherClass()) {
...
*oc = OtherClass(params);
}
This will create a dummy oc when the class it created and then copy or move assign the real object to *oc later. This is wasteful, so why not initialize it with the final value directly:
MyClass() : oc(new OtherClass(params)) {
...
}
or if you have to compute params first:
MyClass : oc(nullptr) {
...
oc = new OtherClass(params);
}
Initializing oc with nullptr first isn't required but it is dirt cheap and it ensures accidentally using oc will access a nullptr and fail instead of oc being some random value that might not crash.
You can also, and better, ensure initialization by inlineing that:
class MyClass {
...
private:
OtherClass *oc(nullptr);
}
With that the compiler will initialize oc whenever you don't initialize it in an initializer list in the constructor.
That said: Do you really need a pointer there? You need a destructor, copy/move constructors and aissgnment operators to handle the pointer directly. An Otherclass oc; would be much easier to deal with.
But if you do need a pointer then you should use a smart pointer to handle ownership for you. That means use std::unique_ptr or more likely std::shared_ptr for it. You might not even need anything past the constructor that way. But think about what it means for copy/move to have the pointer shared. Read about the rule of 0/3/5.
I have not been able to find any documentation about what happens if I pass a null pointer to std::make_unique.
Is an exception thrown?
std::make_unique forwards all the arguments passed to a matching target constructor.
Therefore, if you pass nullptr, it will search for a constructor that accepts nullptr_t (or any matching constructor). If it is not there, it will fail to compile.
class A
{
public:
A(void*) { ... } // could also be A(int*) or template <typename T> A(T*)
};
...
std::make_unique<A>(nullptr); // constructs an A by calling A(void*), passing nullptr.
std::make_unique is meant to construct an object wrapped by forwarding the arguments to the constructor of the wrapped object.
Passing nullptr to it doesn't make any sense in the way you mean. If you need to clear the content of a std::unique_ptr just call reset().
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.
is it possible to restrict class instances to be used only as rvalues (e.g. temporaries)?
for example, I have class Wrapper whose constructor takes A const& and saves this reference in its member. It's a dangerous because lifetime of Wrapper instance cannot be longer than lifetime of A instance, but it's fine if Wrapper is temporary.
I think that even wanting to do this is a sign of a really bad design.
However, you could make all constructors private and make a friend function that returns an rvalue. That should do the trick.
I don't think it would be safe:
const A &a = YourClass( tmp );
YourClass in this case is the class you're looking for which only allow temporary instances, tmp is the temporary value you pass to the constructor.
It's possible (ie: safe, defined behavior) to have a constant reference to a temporary (ie: a), but the temporary itself (such instance of YourClass) has got a reference to tmp which is no longer valid after that expression is evaluated.
Not exactly the answer you are looking for, but have you thought about weak pointers? (for example, boost::weak_ptr). In this case, the original A would be held in a shared_ptr and the Wrapper constructor accepts a weak_ptr. The neat thing with this approach is that, before each usage of the weak_ptr, you can attempt to lock() which will give you a shared_ptr - if that fails, you know that A is gone and Wrapper cannot function... But it's handled cleanly...
This might do the job unless your class has public data members.
Basically, the idea is not to restrict the construction of the wrapper but to make sure that instances can be used (just like you said) only as long as they are temporary values. One can achieve this by overloading all methods and deleting (or making them private) those that refer to const&.
Here's a simple example:
class Wrapper
{
public:
Wrapper() = default;
Wrapper(const std::string& name) : name(name) {}
void process() && { std::cout << "Greetings from " << name << std::endl; }
// Only temporary instances of this class are allowed!
void process() const & = delete;
private:
std::string name;
};
And some use cases:
Wrapper("John").process(); // intended use case
Wrapper j; // create whatever you want
j.process(); // error C2280: 'void Wrapper::process(void) const &': attempting to reference a deleted function
std::move(j).process(); // this is still possible
const Wrapper& t = Wrapper(); // bind the temporary to a const reference - not a problem because ...
t.process(); // error C2280: 'void Wrapper::process(void) const &': attempting to reference a deleted function
The obvious disadvantages are:
You have to overload every public member function.
The error message is delayed and not very informative.
A similar thing has been done in the standard. The make routines for std::reference_wrapper do not accept temporaries.
Note that they considered another subtlety: the overload uses const T&& instead of T&&. This can be important in our case as well. For example, if your wrapper is deliberately designed to be noncopyable and you use make routines such as
const Wrapper make_wrapper();
instead of
Wrapper make_wrapper();
In this case, you might want to replace
void process() &&;
by
void process() const &&;
I'd not bother enforcing this at compile time, as there are always going to be corner cases where this would be overly restrictive, limiting the usefulness of the class, but rather wrap tools like valgrind or Purify so I can spot places where invalidated references are used.
I believe in C++17 and later you can get approximately what you want by doing the following:
Delete the move constructor for your type (and don't define a copy constructor).
Always accept your type by value in APIs.
So, for example:
#include <type_traits>
#include <utility>
// A non-moveable, non-copyable type.
struct CantMove {
CantMove(CantMove&&) = delete;
CantMove(int) {} // Some other constructor
};
static_assert(!std::is_move_constructible_v<CantMove>);
static_assert(!std::is_copy_constructible_v<CantMove>);
// A function that accepts it by value.
bool AcceptByValue(CantMove input) { return true; }
// It's possible to call the value-accepting API when the input is a prvalue
// (which in previous versions of C++ would have been a temporary).
bool unused = AcceptByValue(CantMove(0));
// But it's not possible to call with a named value, even when casted to an
// rvalue reference. This doesn't compile.
CantMove cant_move(0);
bool unused_2 = AcceptByValue(std::move(cant_move));
It's possible to provide the value-accepting function with what we previously called a temporary because guaranteed copy elision says that there isn't even a temporary involved anymore—the only CantMove object created is the function parameter itself, so there is no move- or copy-construction involved. In contrast it's not possible to call with std::move(cant_move) because that would involve move-constructing the function parameter, and the type is not move-constructible.
Of course it's still possible to initialize a CantMove directly:
CantMove foo{0};
But if you own all of the APIs that accept a CantMove and make them all accept by value, then you can't actually do anything with foo afterward. This means it would be hard for a user to do this by mistake and not realize the problem.
Yes, you could.
You would make the constructor and regular copy-constructor/assign private but make the r-value move semantics (C++0x) public.
You would have a static or friend constructor to create the temporary.
In 2003 C++ you would also be able to use this to bind to a const reference.
Of course you'd have the issue that your const reference would probably become invalidated after the statement.
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