Return value optimization and destructor calls - c++

I know that RVO is mostly applied but can I count on it? I have a function that creates an object of class FlagContainer.
class FlagContainer {
public:
~FlagContainer() {
someItem->flag = true;
}
private:
Item * someItem;
}
public FlagContainer createFlagContainer() {
return FlagContainer();
}
After the caller used the container, the flag must be set. So I can do this with the destructor.
{
FlagContainer container = createFlagContainer();
// do something with container
}
When out of scope, the destructor will be called. But can I be sure that the destructor will never be called in createFlagContainer? Is there any way to achieve this?
I would use AVR GCC 4.7.0 compiler.

I know that RVO is mostly applied but can I count on it?
Don't rely on RVO for logic. Put simply, someone compiling your program can switch it off with a command-line option.
Is there any way to achieve this?
Surprisingly, the standard library already gives you this functionality so you don't need to run the risk of implementing it yourself (move constructors and operators are notoriously difficult to get right)
std::unique_ptr with a custom deleter does the job nicely.
#include <iostream>
#include <memory>
#include <cassert>
// test type
struct X
{
bool flag = false;
};
// a custom deleter that sets a flag on the target
struct flag_setter_impl
{
template<class X>
void operator()(X* px) const {
if (px) {
assert(!px->flag);
std::cout << "setting flag!" << std::endl;
px->flag = true;
}
}
};
// a type of unique_ptr which does not delete, but sets a flag
template<class X>
using flag_setter = std::unique_ptr<X, flag_setter_impl>;
// make a flag_stter for x
template<class X>
auto make_flag_setter(X& x) -> flag_setter<X>
{
return flag_setter<X>(&x, flag_setter_impl());
}
// quick test
auto main() -> int
{
using namespace std;
X x;
{
auto fs1 = make_flag_setter(x);
auto fs2 = move(fs1);
}
return 0;
}
but I don't have the STL on my target
Then don't forget your rules of 0, 3, 5
#include <iostream>
#include <memory>
#include <cassert>
// test type
struct X
{
bool flag = false;
};
// a custom deleter that sets a flag on the target
struct flag_setter_impl
{
template<class X>
void operator()(X* px) const {
if (px) {
assert(!px->flag);
std::cout << "setting flag!" << std::endl;
px->flag = true;
}
}
};
// a type of unique_ptr which does not delete, but sets a flag
template<class X>
struct flag_setter
{
flag_setter(X* px) : px(px) {}
flag_setter(const flag_setter&) = delete;
flag_setter(flag_setter&& r) noexcept : px(r.px) { r.px = nullptr; }
flag_setter& operator=(const flag_setter& r) = delete;
flag_setter& operator=(flag_setter&& r)
{
flag_setter tmp(std::move(r));
std::swap(tmp.px, px);
return *this;
}
~flag_setter() noexcept {
flag_setter_impl()(px);
}
private:
X* px;
};
// make a flag_stter for x
template<class X>
auto make_flag_setter(X& x) -> flag_setter<X>
{
return flag_setter<X>(&x);
}
// quick test
auto main() -> int
{
using namespace std;
X x;
{
auto fs1 = make_flag_setter(x);
auto fs2 = move(fs1);
}
return 0;
}

There is no guarantee [yet] that copy-elision is applied. Guaranteed copy-elision is proposed for inclusion into C++17. Whether copy-elision is applied is entirely at the discretion of the compiler (some compilers have an option to entirely disable it, though).
A potential approach avoiding this need might be the use of an essentially unusable type which can be used only as the constructor argument for the type you are interested in being used and to return an object of that type:
class FlagContainerBuilder {
friend class FlagContainer;
public:
FlagContainerBuilder(/* suitable arguments go here */);
// nothing goes here
};
class FlagContainer {
// ...
public:
FlagContainer(FlagContainerBuilder&& builder);
// as before
};
FlagContainerBuilder createFlagContainer() { ... }
This way you avoid the need to potentially destroy a FlagContainer returned from createFlagContainer().

I know that RVO is mostly applied but can I count on it?
No. Compilers are allowed to implement RVO, but not required. You can only count on it, when your compiler promises to do so.

Although this particular case as per standard 12.8/3/p31.1 Copying and moving class objects [class.copy] renders as a context that the compiler can do NRVO (aka copy elision), you can't rely on it. A program that relies on this kind of optimization is effectively non portable.
To ensure move of the object I would define a move constructor and inside I would null the pointer of the other object, while in the destructor I would check whether the pointer is nullptr in order to set its flag true:
class FlagContainer {
public:
FlagContainer(FlagContainer&& other) : someItem(other.someItem) {
other.someItem = nullptr;
}
~FlagContainer() {
if(someItem) someItem->flag = true;
}
Item * someItem;
};
FlagContainer createFlagContainer() {
return FlagContainer();
}
Live Demo

Related

How does std::optional delay initialization? / How is std::optional implemented?

Lately I've taken interest in initialization. One of the things I'm particularly interested in, is std::optional for its ability to initialize an instance of a type after it's been declared. I have tried reading the code inside the optional header, but the code is just too 'bombastic' for me to comprehend.
How is std::optional able to delay the initialization of an object on the stack? I assume it just reserves sizeof(<whichever_type) number of bytes on the stack, and then reinterprets those bytes for the initialization of <whichever_bytes>. But how does it do that specificially? How is it implemented? How can I implement that myself?
Edit: to clarify, I know that std::optional basically has a bool member to keep track of whether the object is initialized or not, and another member, which contains the data.
What I don't understand, however, is how optional is able to manually initialze something.
How is it able to destruct an object? How is it able to reconstruct a new one again after the old one is destructed?
The "obvious" way to represent an std::optional<T> is to use an indication whether the value is set together with a union containing a T, i.e., something like this:
template <typename T>
class optional {
bool isSet = false;
union { T value; };
public:
// ...
};
By default the members in the union are not initialized. Instead, you'll need to use placement new and manual destruction to manage the life-time of the entity within the union. Conceptually that is similar to using an array of bytes but the compiler handles any alignment requirements.
Here a program with some of the operations shown:
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <cassert>
template <typename T>
class optional {
bool isSet = false;
union { T value; };
void destroy() { if (this->isSet) { this->isSet = true; this->value.~T(); } }
public:
optional() {}
~optional() { this->destroy(); }
optional& operator=(T&& v) {
this->destroy();
new(&this->value) T(std::move(v));
this->isSet = true;
return *this;
}
explicit operator bool() const { return this->isSet; }
T& operator*() { assert(this->isSet); return this->value; }
T const& operator*() const { assert(this->isSet); return this->value; }
};
int main()
{
optional<std::string> o, p;
o = "hello";
if (o) {
std::cout << "optional='" << *o << "'\n";
}
}

boost::allocate_unique yields non-deafult constructible and non-move assignable unique_ptrs

I have a question about allocate_unique from Boost. It looks like that resulting unique_ptrs are quite limited - they cannot be default constructed to nullptr without providing a deleter (even an invalid one), and also, move assignment does not work.
Luckily, move construction does work, so I was able to hack around not having move assignment by calling the destructor and move-constructing using placement new.
Is it a defect in Boost that alloc_deleter is not moveable, and thus disables move assignment of these unique_ptrs? Or am I misunderstanding something?
#include <memory>
#include <memory_resource>
#include <boost/smart_ptr/allocate_unique.hpp>
#include <iostream>
using Pma = std::pmr::polymorphic_allocator<std::byte>;
template<typename T> using pmr_deleter = boost::alloc_deleter<T, Pma>;
template<typename T> using pmr_unique_ptr = std::unique_ptr<T, pmr_deleter<T>>;
struct Vertex {
float x = 1;
float y = 2;
float z = 3;
};
int main() {
auto& res = *std::pmr::new_delete_resource();
pmr_deleter<Vertex> d(nullptr);
pmr_unique_ptr<Vertex> v_empty(nullptr, d); // will not default construct without deleter??
pmr_unique_ptr<Vertex> v = boost::allocate_unique<Vertex>(Pma(&res), Vertex{7,8,9});
// v_empty = std::move(v); // operator=(unique_ptr&&) gets deleted because `alloc_deleter` is not moveable!
// We can hack in a move like this:
v_empty.~pmr_unique_ptr<Vertex>();
new (&v_empty) pmr_unique_ptr<Vertex>(v.get(), v.get_deleter());
v.release();
std::cout << v_empty->x << "," << v_empty->y << "," << v_empty->z << std::endl;
return 0;
}
Polymorphic allocators are stateful, which means they cannot be default-constructed - because they wouldn't know about the memory resource they're supposed to work with.
This is not particular to PMR or unique pointers, it will also crop up when e.g. using Boost Interprocess allocators on a vector - you will always have to pass an initializer for the allocator.
Ifff you want a global/singleton memory resource, you can obviously declare a custom deleter that encodes that constant:
template <typename T> struct pmr_deleter : boost::alloc_deleter<T, Pma> {
pmr_deleter()
: boost::alloc_deleter<T, Pma>(std::pmr::new_delete_resource()) {}
};
This would allow the default constructor(s) to work:
pmr_unique_ptr<Vertex> v_empty; // FINE
pmr_unique_ptr<Vertex> v_empty(nullptr); // ALSO FINE
However it comes at the cost of no longer being type-compatible with the allocate_unique factory return type (alloc_deleter).
You can probably pave hack this, but I think it would probably be best to understand the situation before you decide whether that's worth it. (Hint: I don't think it is, because it is precisely the goal of PMR to type erase the allocator difference, trading in runtime state instead. If you go and move all state into the allocator again, you effectively made it a static allocator again, which is where we would have been without PMR anyways)
Other Notes
pmr_deleter<Vertex> d(nullptr);
Is ill-formed, as the argument may never be null. Both the compiler will warn about this at -Wnon-null, just as Asan/UBSan will:
/home/sehe/Projects/stackoverflow/test.cpp:18:34: runtime error: null pointer passed as argument 2, which is declared to never be null
Here is a wrapper I wrote around the specialization of std::unique_ptr<T, boost::alloc_deleter>. The unique pointer returned by boost::allocate_unique is implicitly convertible to the wrapper. The wrapper is default constructible, move-assignable and also has .get() return a raw pointer instead of a boost fancy pointer type (which requires an additional .ptr() to get a raw pointer).
The only downside is that you have to use the wrapper explicitly instead of e.g. auto with boost::allocate_unique.
using Pma = std::pmr::polymorphic_allocator<std::byte>;
template<typename T> using pmr_deleter = boost::alloc_deleter<T, Pma>;
template<typename T> class pmr_unique_ptr : public std::unique_ptr<T, pmr_deleter<T>> {
public:
using std::unique_ptr<T, pmr_deleter<T>>::unique_ptr;
T* get() const { return std::unique_ptr<T, pmr_deleter<T>>::get().ptr(); }
pmr_unique_ptr() : std::unique_ptr<T, pmr_deleter<T>>(nullptr, pmr_deleter<T>(std::pmr::null_memory_resource())) { }
pmr_unique_ptr(decltype(nullptr)) : pmr_unique_ptr() { }
template<typename P>
pmr_unique_ptr(std::unique_ptr<P, pmr_deleter<P>>&& p)
: pmr_unique_ptr(static_cast<T*>(p.get().ptr()), *reinterpret_cast<pmr_deleter<T>*>(&p.get_deleter())) {
p.release();
}
template<>
pmr_unique_ptr(std::unique_ptr<T, pmr_deleter<T>>&& p) : std::unique_ptr<T, pmr_deleter<T>>(std::move(p)) { };
pmr_unique_ptr(T* p, pmr_deleter<T> d) : std::unique_ptr<T, pmr_deleter<T>>(boost::detail::sp_alloc_ptr<T,T *>(1, p), d) { };
pmr_unique_ptr(const pmr_unique_ptr&) = delete;
pmr_unique_ptr(pmr_unique_ptr&& p) : std::unique_ptr<T, pmr_deleter<T>>(std::move(p)) { }
template<typename P> operator pmr_unique_ptr<P>() {
P* basep = static_cast<P*>(get());
pmr_deleter<P> d(*reinterpret_cast<pmr_deleter<P>*>(&this->get_deleter()));
this->release();
return {basep, std::move(d)};
}
pmr_unique_ptr& operator=(pmr_unique_ptr&& other) {
this->std::unique_ptr<T, pmr_deleter<T>>::~unique_ptr();
new (static_cast<std::unique_ptr<T, pmr_deleter<T>>*>(this)) std::unique_ptr<T, pmr_deleter<T>>(std::move(other));
return *this;
}
template<typename P> pmr_unique_ptr& operator=(std::unique_ptr<P, pmr_deleter<P>>&& p) {
return operator=(pmr_unique_ptr(pmr_unique_ptr<P>(std::move(p))));
}
};
Example which compiles:
#include <memory_resource>
#include <boost/smart_ptr/allocate_unique.hpp>
// ... the definitions from above
// ...
pmr_unique_ptr<int> p;
pmr_unique_ptr<int> p2 = nullptr;
p2 = boost::allocate_unique<int>(Pma(std::pmr::new_delete_resource()), 5);
p = std::move(p2);
int *rawp = p.get();

Extend object lifetime/scope from a `if constexpr` branch

Say we have the following code
struct MyClass
{
MyClass() = delete; // or MyClass() { }
MyClass(int) { }
void func() { }
};
int main()
{
if constexpr (std::is_default_constructible_v<MyClass>) {
MyClass myObj;
} else {
MyClass myObj(10);
}
myObj.func(); // Error
}
Here I am using if constexpr to determine whether the class is default-constructible (or not), and then create an object accordingly. In a way, I naively thought this would simplify the different branches down to just the one that's true, i.e.
if constexpr (true) {
/* instruction branch 1 */
} else if constexpr (false) {
/* instruction branch 2 */
}
simply becomes
/* instruction branch 1 */
But in reality, it is probably more like this
{
/* instruction branch 1 */
}
But then the question becomes (going to back to the the very first example), how can I can I keep myObj in scope outside the { ... }?
You can't extend the lifetime of an object with automatic storage duration beyond the scope in which it's created.
What you can do is create uninitialized storage outside your if block and create an object in that storage within the scope of the if. The easiest way to do that is probably std::optional:
template <typename T>
void foo() {
std::optional<T> obj;
if constexpr (std::is_default_constructible_v<T>) {
obj.emplace();
} else {
obj.emplace(10);
}
obj->func();
}
Live Demo
This does result in a small amount of overhead though, since std::optional has to hold an extra flag to determine if it holds an object or not. If you want to avoid that overhead you could manage the storage yourself:
template <typename T>
void foo() {
std::aligned_storage_t<sizeof(T), alignof(T)> storage;
T* ptr;
if constexpr (std::is_default_constructible_v<T>) {
ptr = new(&storage) T{};
} else {
ptr = new(&storage) T{10};
}
struct destroy {
destroy(T* ptr) : ptr_{ptr} {}
~destroy() { ptr_->~T(); }
T* ptr_;
} destroy{ptr};
ptr->func();
}
Live Demo
Note that in both cases I've moved the functionality to a function template. For if constexpr to discard a branch it must be dependent on a template parameter. If you try to do this directly in main the false branch will not be discarded and you will get an error complaining about a missing default constructor.
First, your code won't work. if constexpr really needs its condition to be dependent.
I'll fix it.
template<class MyClass>
void func() {
MyClass myObj = []{
if constexpr (std::is_default_constructible_v<MyClass>) {
return MyClass{};
} else {
return MyClass(10);
}
}();
myObj.func();
}
now
int main() {
func<MyClass>();
}
solves your problem.
Note that under c++17 rules, no copies or moves of MyClass occur in the above code.

RVO with a standard layout struct without any constructors

I have a struct representing a binary message. I want to write a function to get the next such record from a buffer (whether a file or a socket, doesn't matter):
template <typename Record>
Record getNext();
Now, I could write this like:
template <typename Record>
Record getNext() {
Record r;
populateNext(reinterpret_cast<char*>(&r), // maybe ::read()
sizeof(r)); // or equivalent
return r;
}
which is nice and gives me the benefits of RVO. However, it will invoke the default constructor of Record, which may be composed of types with non-trival default constructors which do work that I would like to avoid - these are not necessarily POD types, but they are standard layout.
Is there a way to write getNext() such that we avoid any constructors (default or copy/move) on Record? Ideally, when the user calls:
auto record = getNext<Record>();
The buffer is read directly into the memory of record. Is this possible?
no_init is a constant of type no_init_t.
If you construct a pod from a no_init_t, you get an uninitialized pod, and (assuming elision) there is nothing to be done.
If you construct a non-pod from a no_init_t, you have to override a constructor, and make it not initialize the data. Usually class_name(no_init_t):field1(no_init), field2(no_init){} will do it, and sometimes class_name(no_init_t){} will do it (assuming all contents are pod).
Constructing from no_init on each member can act as a sanity check that the members are indeed pod, however. A non-pod class constructed from no_init will fail to compile until you write the no_init_t constructor.
This (having to no_init each member constructor) does generate some annoying DRY failure, but we don't got reflection, so you are gonna repeat yourself and like it.
namespace {
struct no_init_t {
template<class T, class=std::enable_if_t<std::is_pod<T>{}>>
operator T()const{
T tmp;
return tmp;
}
static no_init_t instance() { return {}; }
no_init_t(no_init_t const&) = default;
private:
no_init_t() = default;
};
static const no_init = no_init_t::instance();
}
struct Foo {
char buff[1000];
size_t hash;
Foo():Foo(""){}
template<size_t N, class=std::enable_if_t< (N<=sizeof(buff)) >>
Foo( char const(&in)[N] ) {
// some "expensive" copy and hash
}
Foo(no_init_t) {} // no initialization!
};
struct Record {
int x;
Foo foo;
Record()=default;
Record(no_init_t):
x(no_init), foo(no_init)
{}
};
Now we can construct Record with no_init and it won't be initialized.
Every POD class is not initialized. Every non-POD class must provide a no_init_t constructor (and presumably implement non-initialization, as best it can).
You then memcpy right over it.
This requires modifying your type, and the types it contains, to support non-initialization.
Something like this?
EDIT:
Addresses comment on alignment. Now uses anonymous union to ensure correct alignment.
TestRecord now incorporates another standard layout type egg
Added proof that even though egg has a default constructor, the class is not constructed prior to being filled by populateNextRecord()
I think this is about as fast as it can be isn't it?
#include <iostream>
#include <array>
#include <algorithm>
struct egg {
egg(int i) : _val(i) {}
egg() {}
int _val = 6;
friend std::ostream& operator<<(std::ostream& os, const egg& e) {
return os << e._val;
}
};
struct TestRecord {
egg x;
double y;
};
void populateNext(uint8_t* first, size_t length)
{
// do work here
TestRecord data_source { 10, 100.2 };
auto source = reinterpret_cast<uint8_t*>(&data_source);
std::copy(source, source + length, first);
}
template<class Record>
struct RecordProxy
{
RecordProxy() {}
uint8_t* data() {
return _data;
}
static constexpr size_t size() {
return sizeof(Record);
}
Record& as_record() {
return _record;
}
union {
Record _record;
uint8_t _data[sizeof(Record)];
};
};
template <typename Record>
RecordProxy<Record> getNext() {
RecordProxy<Record> r;
populateNext(r.data(), // maybe ::read()
r.size()); // or equivalent
return r;
}
using namespace std;
int main()
{
RecordProxy<TestRecord> prove_not_initialised;
auto& r1 = prove_not_initialised.as_record();
cout << "x = " << r1.x << ", y = " << r1.y << endl;
auto buffer = getNext<TestRecord>();
auto& actual_record = buffer.as_record();
cout << "x = " << actual_record.x << ", y = " << actual_record.y << endl;
return 0;
}

auto x = make_x(...) and this

I have a class template S<T> and because the template parameter is sometimes hard write explicitly I also have a little helper function makeS(...) to deduce the template parameter.
Now the problem is that the constructor of S has a "side effect": it adds itself to a map which then will later be used to iterate over all instances of S. But this effectively makes S<T> s{...}; very different from auto s = makeS(...); (if RVO is not used).
Here is some code which hopefully shows what I'm trying to do. (Note: in the actual program, S has more than a single template parameter and all will be deduced in makeS)
#include <cassert>
#include <iostream>
#include <map>
#include <string>
#include <utility>
using namespace std;
struct Base
{
virtual ~Base() {}
virtual void f() const = 0;
};
static map<string, Base*> Map;
template <typename T>
struct S : Base
{
T Func;
Base* This;
S(string const& name, T func) : Func(std::move(func)), This(this)
{
//
// Automatically add this struct to Map...
//
Map.insert({name, this});
}
virtual void f() const override { Func(); }
};
template <typename T>
S<T> makeS(std::string const& name, T func)
{
return S<T>(name, std::move(func));
}
void func1()
{
std::cout << "func1\n";
}
int main()
{
struct Func2
{
void operator ()() const {
std::cout << "func2\n";
}
};
//
// This is not possible:
//
// S< ??? > s("s", [](){});
//
// This would be ok:
//
// auto F = [](){};
// S<decltype(F)> s("s", F);
//
auto s1 = makeS("s1", func1);
auto s2 = makeS("s2", Func2());
auto s3 = makeS("s3", [](){ std::cout << "func3\n"; });
//
// Manually adding s1,... to the Map is ok, but that's what
// I want to avoid...
//
// Map.insert({"s1", &s1});
// ...
//
assert(&s1 == s1.This);
assert(&s2 == s2.This);
assert(&s3 == s3.This);
for (auto&& I : Map)
{
I.second->f();
}
}
As I understand it, the map will only contain valid pointers if RVO is used in auto s1 = makeS(...) etc. and this is not guaranteed.
Is there a way to deduce the template parameters while at the same time avoiding the need to manually register s1,...?
Your basic problem is you failed to implement the rule of 3. If your destructor needs non-trivial behavior (and if you register yourself in the constructor, this is the case), you must either implement or block assignment and copy construct (and/or move-assign and move-construct).
In this case, we can implement a move-construct and block move-assign, and copy construct and copy assign are implicitly blocked.
First, add name to S. Then implement a move constructor.
template <typename T>
struct S : Base
{
std::string Name;
T Func;
Base* This; // ?? why ?? this looks both dangerous and useless at the same time!
S( S&& s ): Name(std::move(s.Name)), Func(std::move(s.Func)), This(this) {
s.clear(); // technically `move` need not clear.
map[Name] = this; // overwrite
}
S& operator=(S&& s) = delete; // or implement it
now your object is moveable, and when moved it updates the Map. ~S should, I assume, unregister from Map -- detect if your name is empty (and assert at construction you gain a non-empty name), and if it is don't unregister (as you where already moved from).
Now move-construct and elided-construct have the same semantics. RVO failure results in some inefficiency, but no logical failure. Plus, your type is now moveable, which tends to be really useful.
If you need to maintain object identity, use can use std::unique_ptr:
template <typename T>
std::unique_ptr<S<T>> makeS(std::string const& name, T func)
{
return { new S<T>(name, std::move(func)) };
}
Now moving the pointer from place to place won't move the object; the pointer kept in the map will stay valid.
My suggestions to improve your code:
1) Get rid of the side effect in the constructor. Create objects in a factory method only (makeS in your code; you can make it a static member function of S) and register the S objects in that method. To disable object creation in different ways make constructor(s) private.
2) Disable S objects copying/moving and handle the objects for example as shared_ptr/unique_ptr<S> only. When you disable copying/moving you avoid the problem when your map contains a pointer to invalid objects and now you don't need to rely on RVO.
3) Use std::function<void()> instead of T Func;. In that case your class don't need to be a template class (or it will have less template arguments). That will simplify your code.