Distinguish between temporaries and non-temporaries in function signature? - c++

I want to create a class Bar that can distinguish between temporaries and non-const non-temporaries. According to this (about 25% down the page), I can get away with this if the second StealPointer takes a const reference (to a pointer in my case), but in my code it just uses the StealPointer(Foo*&& foo) version regardless of how it is called.
class Foo {};
class Bar {
public:
// For when StealPointer(new Foo()); is called. Bar instance then owns the
// pointer.
void StealPointer(Foo*&& foo) {} // Get the temporaries only.
// For when a Foo* already exists and is passed in StealPointer(my_foo_ptr);
// Takes ownership and invalidates the pointer that the caller once had.
void StealPointer(Foo*&) {} // Get lvalues only.
};
Can I do this? Is there a way to do this that only requires one function? If it matters, Bar is going to store the pointer in a unique_ptr and I would like to avoid the additional syntax of passing in a unique_ptr or making the caller do something with std::move. I can't just pass the pointers by reference, because temporaries of type Foo* can't be converted to Foo*&.

Make your function templated and let std::unique_ptr worry about those details for you.
template <typename Ptr>
void StealPointer(Ptr&& p) // a universal reference, matches *any* type of value
{
uniqptr = std::move(p); // Works for both rvalues and lvalues
}

Related

Can a function take an argument by const reference *without* accepting temporaries as an argument?

Motivating example:
Suppose we have a class Foo and a class ObservesFoo that has a non-owning pointer to an instance of a Foo. We could write that as follows:
class Foo {};
class ObservesFoo
{
Foo* p_foo = nullptr;
ObservesFoo(Foo* t_foo) : p_foo(t_foo) { }
};
But maybe I don't consider nullptr to be a valid value for p_foo to have. I can write a different constructor for ObservesFoo that'll enforce this requirement without me needing to do something like throwing if t_foo is invalid:
ObservesFoo(Foo& t_foo) : p_foo(&t_foo) { }
Then I say to myself, "Hang on, this constructor isn't changing t_foo. Shouldn't I take it as a const Foo&?" But then if I write the function like this, it might accept a temporary Foo as its argument, whose lifetime would immediately end as soon as the constructor returned, which would obviate any guarantees I got from ensuring that p_foo wasn't nullptr, because now it could very easily be a dangling pointer.
Is there a clean way to get the middle ground between the Foo& constructor and the const Foo& constructor?
Assuming const doesn't interfere with how you use the object, and you never need to rebind what object is being observed, and the observed Foo instance is guaranteed to outlive its observer(s), then a reference will work.
If you delete the constructor taking an rvalue-reference to a Foo, you will prevent the accidental lifetime extension you mentioned:
class ObservesFoo
{
Foo const& p_foo;
ObservesFoo(Foo const& t_foo) : p_foo(t_foo) { }
ObservesFoo(Foo&& t_foo) = delete;
};

Is there a more concise way to pass function result as shown here

I'd like to call my function that creates the object to be passed to a method on the same line, but if I do I get an initial value of reference to non-const must be an lvalue compile-time error. To get it to compile I need to declare a temporary holder variable and set it equal to the factory method resul.
I realize this is because the reference is simply an address of a storage location, but is C++ really not able to recognize the construct and create a temporary holder variable? I am not a great C++ coder, so I assume I am missing something or that there is a gap in my knowledge, or both - and I hope you can help. I am using GCC, and C++ 11, with VS Code as my editor.
class Bar {};
class Foo {
const Baz add(Bar& bar) { /* .... */ }
};
void doIt() {
Baz baz;
// what compiles...
Bar bar = createBar();
baz.add(bar);
// What I'd like... but it gives me "initial value of reference to non-const must be an lvalue"
// baz.add(createBar());
}
Change the signature to:
Baz add(Bar bar) { /* .... */ }
Since you need to retain the value (to put it in a vector) you must have ownership of the object. You've declared that you take a reference to avoid making a copy, but you do make a copy -- when you put it in the vector!
Instead, take the object by value, and then the caller can move-construct the argument, and add() can move-construct the vector element. (C++17 further guarantees move-elision in some of these cases, so the object could be directly constructed in the vector.)
There is no point in returning the Baz object as const since it's not yours anymore; the caller can decide if they want to receive it as const.
Your doIt() becomes:
void doIt() {
// Argument is move-constructed from the temporary.
baz.add(createBar());
}
In your method:
Baz Foo::add(Bar bar) {
// Perform your checks, then...
the_vector.emplace_back(std::move(bar));
return some_baz;
}
The general advice is that if your function needs to take ownership of an object, it should receive it by value. This allows the caller to decide what they want to do: make a copy because they still need the original, or move the original into the argument and let the callee steal the state.

Force non-null while preserving constness

Consider this class
class Foo
{
public:
Foo()
: r_obj(&nullobj)
{}
void setDataRef(const T& obj)
{
r_obj = &obj;
}
private:
static T nullobj;
const T* r_obj;
};
This forces r_obj to point to something, and I know that the class cannot modify the logical state of whatever it points to. However, now it is perfectly legal to pass a temporary object to setDataRef which is really bad.
If instead using setDataRef(T&), the constness is lost.
Passing a pointer does not force the object to really point to something.
Is it possible to get all three:
Constness
Non-null
Forbid temporary
In old C++ I think this was impossible, but with C++11 we can do:
class Foo
{
public:
Foo()
: r_obj(&nullobj)
{}
void setDataRef(const T& obj)
{
r_obj = &obj;
}
void setDataRef(T&& obj) = delete; // Crucial extra line
private:
static T nullobj;
const T* r_obj;
};
If somebody tries to pass a temporary to setDataRef, overload resolution will prefer the T&& version but because it is deleted, the call is ill-formed. If somebody passes an lvalue, that will bind to the const reference.
Martin's suggestion is very nice and does prevent the caller from passing an rvalue to the function. But that still won't guarantee that the lifetime of the passed object extends beyond the lifetime of the Foo instance.
If the object stores a pointer to the passed object, it would be most intuitive, if a pointer were passed to the setter. Passing a pointer also prevents using an rvalue (because taking an address of an rvalue is not allowed).
Just like you have to document the interface that the passed object must exist as long as Foo instance exists, you can also document that the passed pointer must not be null. Additionally, you can assert it. You could similarly delete the overload for nullptr_t to prevent passing the literal to the setter but that won't prevent passing null pointers of T* type. In my opinion, the advantage of the reference setter is not great.
Of course, neither raw pointer, nor a reference tell the caller anything about ownership, so that must in either case be documented. Instead, I would recommend storing and passing a smart pointer. unique_ptr if Foo should have the ownership (apparently not), shared_ptr if Foo should keep the object alive if it goes out of scope elsewhere, or possibly weak_ptr if Foo should have a failure mode when referred object no longer exists. A smart pointer would be self documenting and intuitive for the user.

What is the correct way to convert a std::unique_ptr to a std::unique_ptr to a superclass?

Suppose I have a class called foo which inherits from a class called bar.
I have a std::unique_ptr to an instance of foo and I want to pass it to a function that only takes std::unique_ptr<bar>. How can I convert the pointer so it works in my function?
You can convert a std::unique_ptr<foo> rvalue to an std::unique_ptr<bar>:
std::unique_ptr<foo> f(new foo);
std::unique_ptr<bar> b(std::move(f));
Obviously, the pointer will be owned by b and if b gets destroyed bar needs to have a virtual destructor.
Nothing special is required because of the inheritance. You need to use std::move to pass the unique_ptr to a function, but this is true even if the types match:
#include <memory>
struct A {
};
struct B : A {
};
static void f(std::unique_ptr<A>)
{
}
int main(int,char**)
{
std::unique_ptr<B> b_ptr(new B);
f(std::move(b_ptr));
}
You may use this syntax:
std::unique_ptr<parent> parentptr = std::unique_ptr<child>(childptr);
Or you may use std::move.
The other option is to emit raw pointer, but you need to change a function:
void func(const parent* ptr)
{
// actions...
}
func(*childptr);
Here is a good article about smart pointers and passing it to functions: http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters.
You can't, because it would violate the most basic unique_ptr rule: there has to be only one instance that holds a given pointer, and the unique_ptr has full ownership of it (when it goes out of scope, the pointee is deleted).
unique_ptr<T> and unique_ptr<U> (where U : T) aren't compatible, as you've seen.
For shared_ptr, for which you can have multiple instances, there is std::static_pointer_cast that behaves just like a static_cast, except that it accepts a shared_ptr and returns another one (and both point to the same object).
If you absolutely need to use a unique_ptr, you'll have to create a function that first disowns your current unique_ptr and puts that pointer into a new one of the right type. You might also need to do the opposite conversion after your function call.

yet another issue with call by value|reference

The question is simple and may has been discussed before, but I could find a clear answer for my case. Assume I pass a pointer object to a function
#include "foo.h"
int main()
{
foo * aFoo = new foo;
bar(aFoo);
delete aFoo;
aFoo = NULL;
return 0;
}
Then the function is written like this
void bar (foo *f)
{
f->insert();
}
The question:
Is that a call by value or call by reference? I know in call by value, there is an overhead for copying the object from main() to bar(). So I want to be sure that is a call by reference.
It is a call by value, where the value of the pointer aFoo is being copied into the function parameter f.
A call by reference is a call where the parameter is a reference, and side-effects on the argument (and not on objects possibly pointed to by that argument) which occur inside the function are visible to the caller when the function returns.
So for instance this is a function accepting a parameter by reference:
void bar(foo*& f)
// ^
// Here you are taking a pointer by reference
While this is a function accepting a parameter by value:
void bar(foo* f)
// ^
// Here you are taking a pointer to foo by value
You are probably puzzled by the fact that taking a foo by reference and writing:
void bar(foo& f)
{
f.insert();
}
Has pretty much the same effect as passing a pointer to the same foo object by value and writing:
void bar(foo* f)
{ // Precondition: f != nullptr
f->insert();
}
However, the two things are conceptually different. While the value/state of the object you passed in the first case can be different when the function returns from the value/state it had before calling the function, in the second case the value of the pointer you provided will be the same as it was before you called bar() - while the object pointed to may have undergone some state change.
Also notice, that a pointer can be null, while a reference is always bound to an object.
In your case, it is call by value in terms of the pointer to Foo formally. But since you pass a pointer to a function instead of the class instance itself, then it is conceptually call by reference in terms of the class instance since the function call does not copy the whole instance but just its pointer.
Foo fooInstance;
// providing a way to point (less formally you can call it refer) to the instance
Foo* fooPointer = &fooInstance;
// this function call is call by value (passing the value of the pointer).
// but you can logically view it as 'call by reference' to fooInstance.
bar(fooPointer);