Let's say I have the function:
void foo(Object& o) {
/* only query o, dont alter it*/
}
Is it possible to call this function only with already constructed objects and have Visual Studio throw a compile error if I call the function with a temporary object?
struct Object {
/*Members*/
}
void foo(Object& o) {
/* only query o, dont alter it*/
}
int main() {
Object o = Object();
foo(o); // allow this
foo(Object()) // but disallow this
}
If your parameter is not const
, the function won't accept temporaries.
If your parameter is const
, the function accepts both temporary and regular objects.
But if you want to prevent that, you can use the following
struct Object{};
void foo(const Object& o) {
/*only query o, don't alter it*/
}
void foo(Object&& ) = delete;
int main() {
Object o;
foo(o); // allow this
foo(Object{}); // but disallow this
}
Live
const &&
overloadA temporary object can have its lifetime extended by binding it to a const
lvalue reference (in a function call), whereas it cannot bind to a non-const
lvalue reference. This means that your original example implements the sought after behaviour (cannot be invoked with temporary objects), but at the cost of making the parameter non-const
(even though the implementation only queries and does not mutate the object). This arguably violates const
correctness.
As your free function API is inspecting an Object
object, you could consider changing it into a member function and use ref-qualifiers to explicitly delete the overload that will be chosen by overload resolution for temporary objects. A first approach could be to simply delete the &&
overload:
struct Object {
// ...
void foo() const & {}
void foo() && = delete;
};
int main() {
Object o = Object();
const Object co = Object();
o.foo();
co.foo();
//Object().foo(); // error: use of deleted function
}
However, this does not prohibit the, albeit somewhat contrived, case of const temporary objects as well as movable from const objects (const xvalues), as the deleted non-const
rvalue ref-qualifier overload is not viable for a const
rvalue argument:
std::move(co).foo(); // Accepted.
static_cast<const Object&&>(Object()).foo(); // Accepted.
Thus, instead of explicitly deleting the &&
overload, we can remove also the corner case by instead explicitly deleting the const &&
overload, as this will also be the overload of choice for non-const
temporary objects:
struct Object {
// ...
void foo() const & {}
void foo() const && = delete;
};
int main() {
Object o = Object();
const Object co = Object();
o.foo();
co.foo();
//std::move(o).foo(); // error: use of deleted function
//std::move(co).foo(); // error: use of deleted function
//Object().foo(); // error: use of deleted function
//static_cast<const volatile Object&&>(Object()).foo(); // error: use of deleted function
}
We may note that the same approach is used e.g. for the std::ref
and std::cref
helper functions of std::reference_wrapper
; from [functional.sym]:
// [refwrap], reference_wrapper // ... template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete; // ...
as you naturally want to delete a reference wrapper for temporary objects.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With