Some smart pointer templates, such as boost::shared_ptr, may be instantiated with void to hold an arbitrary object:
http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/sp_techniques.html#pvoid
Below is a minimal scoped_ptr implementation. When instantiated with void, the compiler complains about an illegal "reference to void" being formed in the dereference operator. It seems the "substitution failure is not an error" (SFINAE) rule does not cover this situation.
How then is it possible to implement a scoped_ptr? In particular, is there an alternative to writing a template specialization? This would cause large code reduplication with a realistic smart pointer implementation.
#include <cstdlib>
template<typename T>
void destroy(T* ptr)
{
delete ptr;
}
class scoped_ptr_impl_base
{
public:
virtual ~scoped_ptr_impl_base() { }
};
template<typename T, typename F>
class scoped_ptr_impl : public scoped_ptr_impl_base
{
public:
scoped_ptr_impl(T* ptr, F dtor)
: m_ptr(ptr), m_dtor(dtor)
{
}
virtual ~scoped_ptr_impl()
{
m_dtor(m_ptr);
}
private:
T* m_ptr;
F m_dtor;
};
template<typename T>
class scoped_ptr
{
public:
explicit scoped_ptr(T* ptr = 0)
: m_ptr(ptr),
m_impl(new scoped_ptr_impl<T, void (*)(T*)>(&destroy<T>))
{
}
template<typename F>
scoped_ptr(T* ptr, F dtor)
: m_ptr(ptr),
m_impl(new scoped_ptr_impl<T, F>(ptr, dtor))
{
}
~scoped_ptr()
{
delete m_impl;
}
T& operator*()
{
return *m_ptr;
}
T* operator->()
{
return m_ptr;
}
private:
T* m_ptr;
scoped_ptr_impl_base* m_impl;
scoped_ptr(const scoped_ptr&);
scoped_ptr& operator=(const scoped_ptr&);
};
int main()
{
scoped_ptr<void> p(std::malloc(1), std::free);
// scoped_ptr.cpp: In instantiation of `scoped_ptr<void>':
// scoped_ptr.cpp:76: instantiated from here
// scoped_ptr.cpp:56: error: forming reference to void
// (g++ 4.3.3)
return 0;
}
You could use a type trait for the reference type:
template<typename T>
struct type_trait
{
typedef T& reference;
};
template<>
struct type_trait<void>
{
typedef void reference;
};
then in your scoped_ptr_impl :
typename type_trait<T>::reference operator*()
{
return *m_ptr;
}
Not sure if void is the right type in the specialisation though . What type do you want it to return?
Related
In his book, Fedor G. Pikus talks about type erasure in C++. Specifically, he mentions the example of a smart pointer which has its own deleter whose type is "abstracted away".
This is the example he gives:
template <typename T>
class smartptr {
struct deleter_base {
virtual void apply(void*) = 0;
virtual ~deleter_base() { }
};
template <typename Deleter>
struct deleter : public deleter_base {
deleter (Deleter d) : d_(d) { }
virtual void apply(void* p) { d_(static_cast<T*>(p)); }
Deleter d_;
};
public:
template <typename Deleter>
smartptr(T* p, Deleter d) : p_(p), d_(new deleter<Deleter>(d)) {}
~smartptr() { d_->apply(p_); delete d_; }
T* operator->() { return p_; }
const T* operator->() const { return p_; }
private:
T* p_;
deleter_base* d_;
};
While I understand the implementation for the most part, there is one small thing I can't seem to figure out.
Why do the virtual apply methods of the deleter_base and deleter class use void* pointers and then static_cast to a pointer of type T* instead of just being of type T* to begin with?
As so:
...
struct deleter_base {
virtual void apply(T*) = 0;
virtual ~deleter_base() { }
};
template <typename Deleter>
struct deleter : public deleter_base {
deleter (Deleter d) : d_(d) { }
virtual void apply(T* p) { d_(p); }
Deleter d_;
};
...
Why do the virtual apply methods of the deleter_base and deleter class use void* pointers and then static_cast to a pointer of type T* instead of just being of type T* to begin with?
The answer to your question is simple: taking this example code in isolation, there is no particular reason to use the void* and static_cast<T*> approach. The code below, with the edits you suggest to replace void* with T*, works just fine.
I prefer to spell it out as T* because it makes the code more clear. It is important to note that the void* approach is still correct -- it is always legal to cast a pointer to void* and back again.
Using T* works because the inner deleter_base class is still a template type parameterized on T. In other words, it is a smartptr<T>::deleter_base and so has full access to T. In fact, no smartptr<T> will ever construct a deleter_base that passes anything but a T* to its apply method.
Using void* is a classic C-style approach to type erasure, so the author may have used it out of habit. Or perhaps an earlier design required void* and it stayed that way after some revision.
For example, Igor Tandetnik raised the possibility that void* leaves room to convert a smartptr<Derived> into a smartptr<Base> while still using the same deleter object. But, to do this the T type must be erased from deleter_base as well, which is not the case here (because it is an inner class of the smartptr<T> template).
However, the apparent point here is to "type erase away" the Deleter type from the smartptr<T>, and not require the user to spell the type smartptr<T, Deleter>. Both approaches under discussion here accomplish this goal.
#include <memory>
template <typename T>
class smartptr {
struct deleter_base {
virtual void apply(T*) = 0;
virtual ~deleter_base() {}
};
template <typename Deleter>
struct deleter : public deleter_base {
deleter(Deleter d) : d_(d) {}
virtual void apply(T* p) { d_(p); }
Deleter d_;
};
public:
template <typename Deleter>
smartptr(T* p, Deleter d) : p_(p), d_(new deleter<Deleter>(d)) {}
~smartptr() {
d_->apply(p_);
delete d_;
}
T* operator->() { return p_; }
const T* operator->() const { return p_; }
private:
T* p_;
deleter_base* d_;
};
void Code() {
smartptr<int> foo(new int, std::default_delete<int>());
}
I'm trying to help with this question, and I think I found a nice solution. But it's kind of complex due to all the wrapping I need.
I would want to overload operator>> to allow for easy chaining notation. But it's not working as I expected.
I can do:
#include <functional>
#include <memory>
template<typename T>
class Object {
private:
T* const ptr{ nullptr };
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using MyObject = Object<int>;
template <typename T, typename MemFn>
auto fnc (T* ptr, MemFn memFn)
-> decltype(std::invoke(memFn, std::declval<T>())) {
if (ptr) return std::invoke(memFn, *ptr);
return nullptr;
}
int main() {
std::unique_ptr<MyObject> myObjectPtr = std::make_unique<MyObject>(nullptr);
[[maybe_unused]] int* optionalTarget = fnc(myObjectPtr.get(), &MyObject::Get);
}
However, I want to do
#include <functional>
#include <memory>
template<typename T>
class Object {
private:
T* const ptr{ nullptr };
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using MyObject = Object<int>;
template <typename T, typename MemFn>
auto operator>> (T* ptr, MemFn memFn)
-> decltype(std::invoke(memFn, std::declval<T>())) {
if (ptr) return std::invoke(memFn, *ptr);
return nullptr;
}
int main() {
std::unique_ptr<MyObject> myObjectPtr = std::make_unique<MyObject>(nullptr);
[[maybe_unused]] int* optionalTarget = myObjectPtr.get() >> &MyObject::Get;
}
What does work, but what I consider ugly is
#include <functional>
#include <memory>
#include <optional>
template<typename T>
class Object {
private:
T* const ptr{ nullptr };
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using MyObject = Object<int>;
template <typename T>
auto makeOptRef(T* ptr) -> std::optional< std::reference_wrapper<T>> {
if (ptr) return std::ref(*ptr);
return {};
}
template <typename T, typename MemFn>
auto operator>> (std::optional<std::reference_wrapper <T>> ptr, MemFn memFn)
-> std::optional<std::reference_wrapper<std::remove_pointer_t<decltype(std::invoke(memFn, std::declval<T>()))>>> {
if (ptr) return makeOptRef(std::invoke(memFn, *ptr));
return {};
}
int main() {
std::unique_ptr<MyObject> myObjectPtr = std::make_unique<MyObject>(nullptr);
std::optional<std::reference_wrapper<MyObject>> myObjOptRef = makeOptRef(myObjectPtr.get());
std::optional<std::reference_wrapper<int>> optionalTarget = myObjOptRef >> &MyObject::Get;
[[maybe_unused]] int output = (optionalTarget) ? optionalTarget->get() : -1;
}
Any pointer type is a built-in type, as are pointers to members. You are trying to overload an operator for two built-in types, that's simply a non-starter. At least one argument must be of a user-defined type, or a reference to it.
You can get it to work by passing a T& instead of a T*.
I have a class with one std::unique_ptr as class member. I was wondering, how to correctly define the copy constructor, since I'm getting the following compiler error message: error C2248: std::unique_ptr<_Ty>::unique_ptr : cannot access private member declared in class 'std::unique_ptr<_Ty>. My class design looks something like:
template <typename T>
class Foo{
public:
Foo(){};
Foo( Bar<T> *, int );
Foo( const Foo<T> & );
~Foo(){};
void swap( Foo<T> & );
Foo<T> operator = ( Foo<T> );
private:
std::unique_ptr<Bar> m_ptrBar;
int m_Param1;
};
template < typename T >
Foo<T>::Foo( const Foo<T> & refFoo )
:m_ptrBar(refFoo.m_ptrBar),
m_Param1(refFoo.m_Param1)
{
// error here!
}
template < typename T >
void Foo<T>::swap( Foo<T> & refFoo ){
using std::swap;
swap(m_ptrBar, refFoo.m_ptrBar);
swap(m_Param1, refFoo.m_Param1);
}
template < typename T >
Foo<T> Foo<T>::operator = ( Foo<T> Elem ){
Elem.swap(*this);
return (*this);
}
Assuming the goal is to copy-construct the uniquely-owned Bar,
template < typename T >
Foo<T>::Foo( const Foo<T> & refFoo )
: m_ptrBar(refFoo.m_ptrBar ? new Bar(*refFoo.m_ptrBar) : nullptr),
m_Param1(refFoo.m_Param1)
{
}
Unique_ptr documentation:
Stores a pointer to an owned object. The object is owned by no other unique_ptr.
The object is destroyed when the unique_ptr is destroyed.
You cant copy it because two objects can't own it.
Try switching to a std::shared_ptr.
EDIT I should point out that this would make both objects have a pointer to that same object. If you want to copy the uniquely owned object Cubbi's solution is the correct one.
A possibility is to create a new clone_ptr type for this.
Below is a rudimentary example of a clone_ptr that invokes the correct copy constructor (and destructor) of a derived object. This is done here by creating a "type erasure" helper when the clone_ptr is created.
Other implementations may be found on the Internet.
#include <memory>
namespace clone_ptr_detail
{
template <class T>
class clone_ptr_helper_base
{
public:
virtual ~clone_ptr_helper_base() {}
virtual T* clone(const T* source) const = 0;
virtual void destroy(const T* p) const = 0;
};
template <class T, class U>
class clone_ptr_helper: public clone_ptr_helper_base<T>
{
public:
virtual T* clone(const T* source) const
{
return new U(static_cast<const U&>(*source));
}
virtual void destroy(const T* p) const
{
delete static_cast<const U*>(p);
}
};
}
template <class T>
class clone_ptr
{
T* ptr;
std::shared_ptr<clone_ptr_detail::clone_ptr_helper_base<T>> ptr_helper;
public:
template <class U>
explicit clone_ptr(U* p): ptr(p), ptr_helper(new clone_ptr_detail::clone_ptr_helper<T, U>()) {}
clone_ptr(const clone_ptr& other): ptr(other.ptr_helper->clone(other.ptr)), ptr_helper(other.ptr_helper) {}
clone_ptr& operator=(clone_ptr rhv)
{
swap(rhv);
return *this;
}
~clone_ptr()
{
ptr_helper->destroy(ptr);
}
T* get() const { /*error checking here*/ return ptr; }
T& operator* () const { return *get(); }
T* operator-> () const { return get(); }
void swap(clone_ptr& other)
{
std::swap(ptr, other.ptr);
ptr_helper.swap(other.ptr_helper);
}
};
See usage example: http://ideone.com/LnWa3
(But perhaps you don't really need to copy your objects, and might rather explore the possibilities of move semantics. For example, you can have a vector<unique_ptr<T>>, as long as you don't use functions that copy the contents.)
Why am I getting this error:
Error 1 error C2662: 'Allocator::Allocate' : cannot convert 'this' pointer from 'const Allocator' to 'Allocator &' ?
Thats code:
/*Allocator.h*/
/*Not finished yet but working*/
#pragma once
template<class T>
class Allocator
{
public:
//typedef T value_type;
typedef T* pointer;
pointer Allocate(std::size_t count);
pointer Construct(void* address, const pointer obj);
template<class FwdIter>
void Destroy_(FwdIter first,FwdIter last);
void Deallocate_(void* where);
Allocator();
~Allocator();
private:
void Destroy_(const T* obj);
};
/*Allocator_impl.hpp*/
#pragma once
#include "StdAfx.h"
#include "Allocator.h"
template<class T>
Allocator<T>::Allocator()
{
}
template<class T>
Allocator<T>::~Allocator()
{
/*Destroy();
Deallocate();*/
}
template<class T>
typename Allocator<T>::pointer Allocator<T>::Allocate(std::size_t count)
{
return static_cast<pointer>(::operator new(sizeof(value_type) * count));
}
template<class T>
typename Allocator<T>::pointer Allocator<T>::Construct(void* address, const pointer obj)
{
return new (address) T(*obj);
}
//template<class T>
//void Allocator<T>::Destroy()
//{
// //Destroy_(addressBegin_, addressBegin_ + size_);
//}
template<class T>
void Allocator<T>::Destroy_(const T* obj)
{
obj->~T();
}
template<class T>
template<class FwdIter>
void Allocator<T>::Destroy_(FwdIter first,FwdIter last)
{
while (first != last)
{
Destroy_(&*first);
++first;
}
}
template<class T>
void Allocator<T>::Deallocate_(void* address)
{
::operator delete(address);
}
//template<class T>
//void Allocator<T>::Deallocate()
//{
// //Deallocate_(addressBegin_);
//}
/*Toy.h*/
#pragma once
#include "Allocator_impl.hpp"
/*As a base to managed memory*/
template<class T, class A = Allocator<T>>
class ToyBase
{
typedef T* pointer;
private:
A alloc_;
protected:
//--------------------------------------COMMENT HERE
pointer Allocate(const std::size_t)const;<------------When invoking this fnc from
explicit ToyBase();
virtual ~ToyBase();
};
template<class T, class A>
ToyBase<T,A>::ToyBase()
{}
template<class T, class A>
ToyBase<T,A>::~ToyBase()
{}
//--------------------------------------AND COMMENT HERE
template<class T, class A>
typename ToyBase<T,A>::pointer ToyBase<T,A>::Allocate(const std::size_t count)const
{
return alloc_.Allocate(count);<-----------here
}
/*
But when I remove const from fnc decl. it works. I do not understand it as I do not change an object merely invoke fnc on its member.
*/
template<class T>
class ToyRepresentation : private ToyBase<T>
{
public:
typedef T value_type;
typedef T* pointer;
ToyRepresentation(const std::size_t = 0);
void Push(T*);
void Pop();
void GetSize()const;
void GetCapacity()const;
void SetCapacity(const std::size_t);
void Reset();
private:
pointer data_;
std::size_t size_;
std::size_t capacity_;
static unsigned TOTAL_; //total number of created objects
};
template<class T>
unsigned ToyRepresentation<T>::TOTAL_ = 0;
template<class T>
ToyRepresentation<T>::ToyRepresentation(const std::size_t count = 0): ToyBase<T>(), data_(Allocate(count)), size_(0), capacity_(count)
{
}
/*tmain*/
#include "stdafx.h"
#include "Toy.h"
int _tmain(int argc, _TCHAR* argv[])
{
try
{
ToyRepresentation<int> t;
}
catch(const std::exception&)
{
}
return 0;
}
Comments to interesting lines are marked in code. Thanks.
alloc_.Allocate is not a const method. You can "fix" (or hide) this warning by making alloc_ mutable, although this should not be done without understanding of why the compiler is warning you.
Not sure why the method that calls this needs to be const anyhow. Allocation is not something that typically is expected to leave its context unchanged.
ToyBase<T,A>::Allocate is const qualified which means that you cannot invoke any non-const methods on any members of this as they too are const-qualified now.
Try FAQ 18.10.
I'm trying to write a custom STL allocator that is derived from std::allocator, but somehow all calls to allocate() go to the base class. I have narrowed it down to this code:
template <typename T> class a : public std::allocator<T> {
public:
T* allocate(size_t n, const void* hint = 0) const {
cout << "yo!";
return 0;
}
};
int main()
{
vector<int, a<int>> v(1000, 42);
return 0;
}
I expect "Yo!" to get printed, followed by some horrible error because I don't actually allocate anything. Instead, the program runs fine and prints nothing. What am I doing wrong?
I get the same results in gcc and VS2008.
You will need to provide a rebind member template and the other stuff that is listed in the allocator requirements in the C++ Standard. For example, you need a template copy constructor which accepts not only allocator<T> but also allocator<U>. For example, one code might do, which a std::list for example is likely to do
template<typename Allocator>
void alloc1chunk(Allocator const& alloc) {
typename Allocator::template rebind<
wrapper<typename Allocator::value_type>
>::other ot(alloc);
// ...
}
The code will fail if there either exist no correct rebind template, or there exist no corresponding copy constructor. You will get nowhere useful with guessing what the requirements are. Sooner or later you will have to do with code that relies on one part of those allocator requirements, and the code will fail because your allocator violates them. I recommend you take a look at them in some working draft your your copy of the Standard in 20.1.5.
In this case, the problem is that I didn't override the rebind member of the allocator. This version works (in VS2008):
template <typename T> class a : public std::allocator<T> {
public:
T* allocate(size_t n, const void* hint = 0) const {
cout << "yo!";
return 0;
}
template <typename U> struct rebind
{
typedef a<U> other;
};
};
int main() {
vector<int, a<int>> v(1000, 42);
return 0;
}
I found this by debugging through the STL headers.
Whether this works or not will be completely dependent on the STL implementation though, so I think that ultimately, Klaim is right in that this shouldn't be done this way.
I have two templates for creating customized allocators; the first works automagically if it is used on a custom type:
template<>
class std::allocator<MY_TYPE>
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef MY_TYPE* pointer;
typedef const MY_TYPE* const_pointer;
typedef MY_TYPE& reference;
typedef const MY_TYPE& const_reference;
typedef MY_TYPE value_type;
template <class U>
struct rebind
{
typedef std::allocator<U> other;
};
pointer allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
{
return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
}
void construct(pointer p, const_reference val)
{
::new(p) T(val);
}
void destroy(pointer p)
{
p->~T();
}
void deallocate(pointer p, size_type n)
{
FREE_FUNC(p);
}
size_type max_size() const throw()
{
// return ~size_type(0); -- Error, fixed according to Constantin's comment
return std::numeric_limits<size_t>::max()/sizeof(MY_TYPE);
}
};
The second is used when we want to have our own allocator for a predefined type with a standard allocator, for instance char, wchar_t, std::string, etc.:
namespace MY_NAMESPACE
{
template <class T> class allocator;
// specialize for void:
template <>
class allocator<void>
{
public:
typedef void* pointer;
typedef const void* const_pointer;
// reference to void members are impossible.
typedef void value_type;
template <class U>
struct rebind
{
typedef allocator<U> other;
};
};
template <class T>
class allocator
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
template <class U>
struct rebind
{
typedef allocator<U> other;
};
allocator() throw()
{
}
template <class U>
allocator(const allocator<U>& u) throw()
{
}
~allocator() throw()
{
}
pointer address(reference r) const
{
return &r;
}
const_pointer address(const_reference r) const
{
return &r;
}
size_type max_size() const throw()
{
// return ~size_type(0); -- Error, fixed according to Constantin's comment
return std::numeric_limits<size_t>::max()/sizeof(T);
}
pointer allocate(size_type n, allocator<void>::const_pointer hint = 0)
{
return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
}
void deallocate(pointer p, size_type n)
{
FREE_FUNC(p);
}
void construct(pointer p, const_reference val)
{
::new(p) T(val);
}
void destroy(pointer p)
{
p->~T();
}
};
template <class T1, class T2>
inline
bool operator==(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
return true;
}
template <class T1, class T2>
inline
bool operator!=(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
return false;
}
}
The first template above, for your own defined type, does not require any further handling but is used automatically by the standard container classes. The second template requires further work when used on a standard type. For std::string, for example, one have to use the following construct when declaring variables of that type (it is simplest with a typedef):
std::basic_string<char>, std::char_traits<char>, MY_NAMESPACE::allocator<char> >
The following code prints "yo" as expected - what you were seeing was our old friend "undefined behaviour".
#include <iostream>
#include <vector>
using namespace std;
template <typename T> class a : public std::allocator<T> {
public:
T* allocate(size_t n, const void* hint = 0) const {
cout << "yo!";
return new T[10000];
}
};
int main()
{
vector<int, a<int> > v(1000, 42);
return 0;
}
Edit: I just checked out the C++ Standard regarding the default allocator. There is no prohibition on inheriting from it. In fact, as far as I'm aware, there is no such prohibition in any part of the Standard.