I have the following example:
#include <cstdint>
class A
{
public:
A(const A&) = delete;
A& operator = (const A&) = delete;
A(A&&) = default; // why do I need the move constructor
A&& operator = (A&&) = delete;
explicit A(uint8_t Val)
{
_val = Val;
}
virtual ~A() = default;
private:
uint8_t _val = 0U;
};
class B
{
public:
B(const B&) = delete;
B& operator = (const B&) = delete;
B(B&&) = delete;
B&& operator = (B&&) = delete;
B() = default;
virtual ~B() = default;
private:
A _a = A(4U); // call the overloaded constructor of class A
};
int main()
{
B b;
return 0;
}
why do I need the move-constructor "A(A&&) = default;" in A? I could not the code line where the mentioned move-constructor is called.
Many thanks in advance.
A _a = A(4U) in this case depends on the move constructor.
This type of initialization is called copy initialization, and according to the standard it may invoke the move constructor, see 9.3/14.
After referred Qt Singleton implementation. I got two solutions:
The first, using template:
template <typename T>
class Singleton
{
friend T;
public:
static T& instance();
private:
Singleton() = default;
~Singleton() = default;
Singleton( const Singleton& ) = delete;
Singleton& operator=( const Singleton& ) = delete;
};
template <typename T>
T& Singleton<T>::instance()
{
static T inst;
return inst;
}
class SingletonTest : public Singleton<SingletonTest>
{
public:
void foo(){
qDebug()<<"FOO"<<this;
}
};
The second using macro:
#define SingletonClass(className) \
public: \
className(className const&) = delete; \
className& operator=(className const&) = delete; \
static className& instance() { \
static className instance; \
return instance; \
} \
private: \
className() = default; \
~className() = default;
class SingletonTest
{
SingletonClass(SingletonTest)
public:
void foo(){
qDebug()<<"FOO"<<this;
}
};
About the first one, it allows instance creation like SingletonTest t, as a Singleton class I think that should not be allowed.
Then I think about the second one, it solves the first problem, but it has to pass the class name into the macro.
So which implementation is better?
Anyway to avoid passing the class name?
Is that wrong to return pointer of instance() function?
I am still little bit confused by returning a const reference. Probably, this has been already discussed, however let's have following code as I did not find the same:
#include <vector>
#include <iostream>
struct A
{
int dataSize;
std::vector<char> data;
};
class T
{
public:
T();
~T();
const A& GetData();
private:
A dataA;
};
T::T() : dataA{1}
{
}
T::~T()
{
}
const A& T::GetData()
{
return dataA;
}
void main()
{
T t;
A dataReceivedCpy = {};
dataReceivedCpy = t.GetData();
const A& dataReceivedRef = t.GetData();
std::cout << dataReceivedRef.dataSize << std::endl;
}
What exactly happens, when I call
dataReceivedCpy = t.GetData();
Is this correct? From my point of view, it is and a copy of the requested struct is made, am I right?
On the other hand,
const A& dataReceivedRef = t.GetData();
returns a reference to object member, it is correct, unless the T object is not destructed. Am I right?
Yes, your understanding sounds correct.
dataReceivedCpy = t.GetData();
calls a copy assignment operator to put a copy of t.dataA in dataReceivedCpy.
const A& dataReceivedRef = t.GetData();
does no copying and makes dataReceivedRef a reference to t.dataA. It is valid for the lifetime of t.
I'm wanting to quickly implement what some call an "owner pointer", that is, a smart pointer ensuring unique ownership semantics, while providing "observer" pointers that don't keep the object alive, but can test whether it is.
The most straightforward way I'm trying to do it is to subclass std::shared_ptr, and disable its copy-construction so that no other pointer can actually share the object.
This is what I have for now :
#include <memory>
#include <iostream>
template <class T>
struct owner_ptr : public std::shared_ptr<T> {
// Import constructors
using std::shared_ptr<T>::shared_ptr;
// Disable copy-construction
owner_ptr(owner_ptr<T> const&) = delete;
// Failed attempt at forbidding what comes next
operator std::shared_ptr<T> const&() = delete;
};
struct Foo {
Foo() {
std::cout << "Hello Foo\n";
}
~Foo() {
std::cout << "G'bye Foo\n";
}
void talk() {
std::cout << "I'm talkin'\n";
}
};
owner_ptr<Foo> fooPtr(new Foo);
int main(int, char**) {
// This should not compile, but it does.
std::shared_ptr<Foo> sptr = fooPtr;
// Simple tests
fooPtr->talk();
(*fooPtr).talk();
// Confirmation that two pointers are sharing the object (it prints "2").
std::cout << sptr.use_count() << '\n';
}
I've been pulling my hair on this one. How do I forbid the copy-construction of a std::shared_ptr from my owner_ptr ? I'm not fond of inheriting privately and then importing everything from std::shared_ptr...
I don't think subclassing std::shared_ptr is the way to go. If you really wanted to do it properly I think you should implement it yourself including all the reference counting. Implementing a smart pointer is not actually that hard.
However, in most cases, if you just want something that meets your needs use composition.
I was curious about what you were trying to do, I'm not convinced it is a good idea but I had a go at implementing a OwnerPointer and ObserverPointer pair using composition:
#include <memory>
#include <iostream>
struct Foo {
Foo() {std::cout << "Hello Foo\n"; }
~Foo() { std::cout << "G'bye Foo\n"; }
void talk() { std::cout << "I'm talkin'\n"; }
};
template <class T>
class ObserverPointer; // Forward declaration.
template<class T>
class OwnerPointer; // Forward declaration.
// RAII object that can be obtained from ObserverPointer
// that ensures the ObserverPointer does not expire.
// Only operation is to test validity.
template <class T>
class ObserverLock {
friend ObserverPointer<T>;
private:
std::shared_ptr<T> impl_;
ObserverLock(const std::weak_ptr<T>& in) : impl_(in.lock()) {}
public:
// Movable.
ObserverLock(ObserverLock&&) = default;
ObserverLock& operator=(ObserverLock&&) = default;
// Not copyable.
ObserverLock& operator=(const ObserverLock&) = delete;
ObserverLock(const ObserverLock&) = delete;
// Test validity.
explicit operator bool() const noexcept { return impl_ != nullptr;}
};
template <class T>
class ObserverPointer {
private:
std::weak_ptr<T> impl_;
T* raw_;
public:
ObserverPointer(const OwnerPointer<T>& own) noexcept : impl_(own.impl_), raw_(own.get()) {}
T* get() const { return raw_; }
T* operator->() const { return raw_; }
T& operator*() const { return *raw_; }
ObserverPointer() : impl_(), raw_(nullptr) { }
ObserverPointer(const ObserverPointer& in) = default;
ObserverPointer(ObserverPointer&& in) = default;
ObserverPointer& operator=(const ObserverPointer& in) = default;
ObserverPointer& operator=(ObserverPointer&& in) = default;
bool expired() { return impl_.expired(); }
ObserverLock<T> lock() { return ObserverLock<T>(impl_); }
};
template <class T>
struct OwnerPointer {
friend ObserverPointer<T>;
private:
std::shared_ptr<T> impl_;
public:
// Constructors
explicit OwnerPointer(T* in) : impl_(in) {}
template<class Deleter>
OwnerPointer(std::unique_ptr<T, Deleter>&& in) : impl_(std::move(in)) { }
OwnerPointer(std::shared_ptr<T>&& in) noexcept : impl_(std::move(in)) { }
OwnerPointer(OwnerPointer<T>&&) noexcept = default;
OwnerPointer(OwnerPointer<T> const&) = delete;
// Assignment operators
OwnerPointer& operator=(OwnerPointer<T> const&) = delete;
OwnerPointer& operator=(OwnerPointer<T>&&) = default;
T* get() const { return impl_.get(); }
T* operator->() const { return impl_.get(); }
T& operator*() const { return *impl_; }
explicit operator ObserverPointer<T>() const noexcept { return ObserverPointer<T>(impl_);}
explicit operator bool() const noexcept { return impl_;}
};
// Convenience function equivalent to make_shared
template <class T, class... Args>
OwnerPointer<T> make_owner(Args && ...args) {
return OwnerPointer<T>(new T(std::forward<Args>(args)...));
}
int main() {
auto owner = make_owner<Foo>();
ObserverPointer<Foo> observer = owner;
auto lock = observer.lock();
if (lock)
observer->talk();
}
Live demo.
It probably needs some work and it doesn't offer the full feature set of std::shared_ptr & std::weak_ptr but then in most cases it won't need to, just create what you need.
I've stretched the definition of "unique ownership" by offering an RAII ObserverLock object that can only be used to keep the ObserverPointer alive. Technically it "owns" the pointer but it is very restricted in what it can do and you can't create more than one "OwnerPointer".
In std::map, this ends up causing an error when the first object is constructed. I've checked the debugger, and I see that free_list::init() creates the consecutive memory addresses correctly. I'm aware this allocator cannot be used in vector or other related containers, but it's only meant to work with the nodular containers.
I get a run-time error from this in xutility (in VC12), at line 158:
_Container_proxy *_Parent_proxy = _Parent->_Myproxy;
Checking the debugger, it appears that _Parent was never initialized, bringing about the 0xC0000005 run-time error. Why or how it didn't get initialized and why this occurred when the first object was being constructed (after std::map did 3 separate allocations), I do not know.
I would like to have this work with std::map and std::list and the other nodular containers and am not worried about whether it can perform in std::vector, etc.
#include <algorithm>
class free_list {
public:
free_list() {}
free_list(free_list&& other)
: m_next(other.m_next) {
other.m_next = nullptr;
}
free_list(void* data, std::size_t num_elements, std::size_t element_size) {
init(data, num_elements, element_size);
}
free_list& operator=(free_list&& other) {
m_next = other.m_next;
other.m_next = nullptr;
}
void init(void* data, std::size_t num_elements, std::size_t element_size) {
union building {
void* as_void;
char* as_char;
free_list* as_self;
};
building b;
b.as_void = data;
m_next = b.as_self;
b.as_char += element_size;
free_list* runner = m_next;
for (std::size_t s = 1; s < num_elements; ++s) {
runner->m_next = b.as_self;
runner = runner->m_next;
b.as_char += element_size;
}
runner->m_next = nullptr;
}
free_list* obtain() {
if (m_next == nullptr) {
return nullptr;
}
free_list* head = m_next;
m_next = head->m_next;
return head;
}
void give_back(free_list* ptr) {
ptr->m_next = m_next;
m_next = ptr;
}
free_list* m_next;
};
template<class T>
class pool_alloc {
typedef pool_alloc<T> myt;
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef std::false_type propagate_on_container_copy_assignment;
typedef std::true_type propagate_on_container_move_assignment;
typedef std::true_type propagate_on_container_swap;
template<class U> struct rebind {
typedef pool_alloc<U> other;
};
~pool_alloc() {
destroy();
}
pool_alloc() : data(nullptr), fl(), capacity(4096) {
}
pool_alloc(size_type capacity) : data(nullptr), fl(), capacity(capacity) {}
pool_alloc(const myt& other)
: data(nullptr), fl(), capacity(other.capacity) {}
pool_alloc(myt&& other)
: data(other.data), fl(std::move(other.fl)), capacity(other.capacity) {
other.data = nullptr;
}
template<class U>
pool_alloc(const pool_alloc<U>& other)
: data(nullptr), fl(), capacity(other.max_size()) {}
myt& operator=(const myt& other) {
destroy();
capacity = other.capacity;
}
myt& operator=(myt&& other) {
destroy();
data = other.data;
other.data = nullptr;
capacity = other.capacity;
fl = std::move(other.fl);
}
static pointer address(reference ref) {
return &ref;
}
static const_pointer address(const_reference ref) {
return &ref;
}
size_type max_size() const {
return capacity;
}
pointer allocate(size_type) {
if (data == nullptr) create();
return reinterpret_cast<pointer>(fl.obtain());
}
void deallocate(pointer ptr, size_type) {
fl.give_back(reinterpret_cast<free_list*>(ptr));
}
template<class... Args>
static void construct(pointer ptr, Args&&... args) {
::new (ptr) T(std::forward<Args>(args)...);
}
static void destroy(pointer ptr) {
ptr->~T();
}
bool operator==(const myt& other) const {
return reinterpret_cast<char*>(data) ==
reinterpret_cast<char*>(other.data);
}
bool operator!=(const myt& other) const {
return !operator==(other);
}
private:
void create() {
data = ::operator new(capacity * sizeof(value_type));
fl.init(data, capacity, sizeof(value_type));
}
void destroy() {
::operator delete(data);
data = nullptr;
}
void* data;
free_list fl;
size_type capacity;
};
template<>
class pool_alloc < void > {
public:
template <class U> struct rebind { typedef pool_alloc<U> other; };
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
};
The problem comes when std::pair is being constructed (in MSVC12 utility at line 214):
template<class _Other1,
class _Other2,
class = typename enable_if<is_convertible<_Other1, _Ty1>::value
&& is_convertible<_Other2, _Ty2>::value,
void>::type>
pair(_Other1&& _Val1, _Other2&& _Val2)
_NOEXCEPT_OP((is_nothrow_constructible<_Ty1, _Other1&&>::value
&& is_nothrow_constructible<_Ty2, _Other2&&>::value))
: first(_STD forward<_Other1>(_Val1)),
second(_STD forward<_Other2>(_Val2))
{ // construct from moved values
}
Even after stepping in, the run-time error occurs, the same as described above with _Parent not being initialized.
I was able to answer my own question through extensive debugging. Apparently, VC12's std::map implementation at least at times will cast an _Alnod (permanent allocator that stays in scope for the life of the map, which is used to allocate and deallocate the nodes in the map, what I'd expect to be what actually calls allocate() and deallocate()) as an _Alproxy, a temporary allocator which creates some sort of object called _Mproxy (or something like that) using allocate(). The problem, though, is that VC12's implementation then lets _Alproxy go out of scope while still expecting the pointer to the allocated object to remain valid, so it is clear then that I would have to use ::operator new and ::operator delete on an object like _Mproxy: using a memory pool that then goes out of scope while a pointer to a location in it remains is what causes the crash.
I came up with what I suppose could be called a dirty trick, a test that is performed when copy-constructing or copy-assigning an allocator to another allocator type:
template<class U>
pool_alloc(const pool_alloc<U>& other)
: data(nullptr), fl(), capacity(other.max_size()), use_data(true) {
if (sizeof(T) < sizeof(U)) use_data = false;
}
I added the bool member use_data to the allocator class, which if true means to use the memory pool and which if false means to use ::operator new and ::operator delete. By default, it is true. The question of its value arises when the allocator gets cast as another allocator type whose template parameter's size is smaller than that of the source allocator; in that case, use_data is set to false. Because this _Mproxy object or whatever it's called is rather small, this fix seems to work, even when using std::set with char as the element type.
I've tested this using std::set with type char in both VC12 and GCC 4.8.1 in 32-bit and have found that in both cases it works. When allocating and deallocating the nodes in both cases, the memory pool is used.
Here is the full source code:
#include <algorithm>
class free_list {
public:
free_list() {}
free_list(free_list&& other)
: m_next(other.m_next) {
other.m_next = nullptr;
}
free_list(void* data, std::size_t num_elements, std::size_t element_size) {
init(data, num_elements, element_size);
}
free_list& operator=(free_list&& other) {
if (this != &other) {
m_next = other.m_next;
other.m_next = nullptr;
}
return *this;
}
void init(void* data, std::size_t num_elements, std::size_t element_size) {
union building {
void* as_void;
char* as_char;
free_list* as_self;
};
building b;
b.as_void = data;
m_next = b.as_self;
b.as_char += element_size;
free_list* runner = m_next;
for (std::size_t s = 1; s < num_elements; ++s) {
runner->m_next = b.as_self;
runner = runner->m_next;
b.as_char += element_size;
}
runner->m_next = nullptr;
}
free_list* obtain() {
if (m_next == nullptr) {
return nullptr;
}
free_list* head = m_next;
m_next = head->m_next;
return head;
}
void give_back(free_list* ptr) {
ptr->m_next = m_next;
m_next = ptr;
}
free_list* m_next;
};
template<class T>
class pool_alloc {
typedef pool_alloc<T> myt;
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef std::false_type propagate_on_container_copy_assignment;
typedef std::true_type propagate_on_container_move_assignment;
typedef std::true_type propagate_on_container_swap;
myt select_on_container_copy_construction() const {
return *this;
}
template<class U> struct rebind {
typedef pool_alloc<U> other;
};
~pool_alloc() {
clear();
}
pool_alloc() : data(nullptr), fl(), capacity(4096), use_data(true) {}
pool_alloc(size_type capacity) : data(nullptr), fl(),
capacity(capacity), use_data(true) {}
pool_alloc(const myt& other)
: data(nullptr), fl(), capacity(other.capacity),
use_data(other.use_data) {}
pool_alloc(myt&& other)
: data(other.data), fl(std::move(other.fl)), capacity(other.capacity),
use_data(other.use_data) {
other.data = nullptr;
}
template<class U>
pool_alloc(const pool_alloc<U>& other)
: data(nullptr), fl(), capacity(other.max_size()), use_data(true) {
if (sizeof(T) < sizeof(U)) use_data = false;
}
myt& operator=(const myt& other) {
if (*this != other) {
clear();
capacity = other.capacity;
use_data = other.use_data;
}
}
myt& operator=(myt&& other) {
if (*this != other) {
clear();
data = other.data;
other.data = nullptr;
capacity = other.capacity;
use_data = other.use_data;
fl = std::move(other.fl);
}
return *this;
}
template<class U>
myt& operator=(const pool_alloc<U>& other) {
if (this != reinterpret_cast<myt*>(&other)) {
capacity = other.max_size();
if (sizeof(T) < sizeof(U))
use_data = false;
else
use_data = true;
}
return *this;
}
static pointer address(reference ref) {
return &ref;
}
static const_pointer address(const_reference ref) {
return &ref;
}
size_type max_size() const {
return capacity;
}
pointer allocate(size_type) {
if (use_data) {
if (data == nullptr) create();
return reinterpret_cast<pointer>(fl.obtain());
} else {
return reinterpret_cast<pointer>(::operator new(sizeof(T)));
}
}
void deallocate(pointer ptr, size_type) {
if (use_data) {
fl.give_back(reinterpret_cast<free_list*>(ptr));
} else {
::operator delete(reinterpret_cast<void*>(ptr));
}
}
template<class... Args>
static void construct(pointer ptr, Args&&... args) {
::new ((void*)ptr) value_type(std::forward<Args>(args)...);
}
static void destroy(pointer ptr) {
ptr->~value_type();
}
bool operator==(const myt& other) const {
return reinterpret_cast<char*>(data) ==
reinterpret_cast<char*>(other.data);
}
bool operator!=(const myt& other) const {
return !operator==(other);
}
private:
void create() {
size_type size = sizeof(value_type) < sizeof(free_list*) ?
sizeof(free_list*) : sizeof(value_type);
data = ::operator new(capacity * size);
fl.init(data, capacity, size);
}
void clear() {
::operator delete(data);
data = nullptr;
}
void* data;
free_list fl;
size_type capacity;
bool use_data;
};
template<>
class pool_alloc < void > {
public:
template <class U> struct rebind { typedef pool_alloc<U> other; };
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
};
template<class Container, class Alloc>
void change_capacity(Container& c, typename Alloc::size_type new_capacity) {
Container temp(c, Alloc(new_capacity));
c = std::move(temp);
}
Since the allocator is not automatic-growing (don't know how to make such a thing), I have added the change_capacity() function.