Opened 14 years ago

Closed 9 years ago

#2481 closed Feature Requests (fixed)

make_shared and allocate_shared friendship

Reported by: Michael Marcin <mike.marcin@…> Owned by: Peter Dimov
Milestone: To Be Determined Component: smart_ptr
Version: Boost 1.36.0 Severity: Problem
Keywords: Cc: jwakely.boost@…

Description

It is currently vary difficult to have a class with private constructors use make_shared and allocate_shared. This same classes are some ideal candidates for private constructors because any class that derives from enable_shared_from_this should probably have private constructors to prevent misuse. Simplifying friend declarations for these functions would be very helpful.

For example:

class Foo : public enable_shared_from_this<Foo>
{
  friend class make_shared_access;
  Foo();
};

int main()
{
  shared_ptr<Foo> foo( make_shared<Foo>() );
}

Attachments (1)

make_shared_access.patch (24.7 KB ) - added by Michael Marcin <mike.marcin@…> 14 years ago.

Download all attachments as: .zip

Change History (10)

by Michael Marcin <mike.marcin@…>, 14 years ago

Attachment: make_shared_access.patch added

comment:1 by Michael Marcin <mike.marcin@…>, 14 years ago

Added a brute force patch that implements this for c++03 against boost 1.36 release. I left variadic template support as a todo because I don't know the syntax and don't have a compiler for it.

comment:2 by Frank Mori Hess, 14 years ago

One limitation of your solution: suppose I have private constructors and a static factory function for a class, and the factory function returns a shared_ptr. If I make make_shared_access a friend, it opens a hole where people can still create objects of the class without using the factory function by using make_shared or allocate_shared directly.

comment:3 by Jonathan Wakely <jwakely.boost@…>, 14 years ago

Cc: jwakely.boost@… added

Right, you don't even need the make_shared_access extension to open that hole, the design is compromised if you declare make_shared or allocate_shared as a friend.

Given:

class MustUseFactory {
  MustUseFactory() { }
public:
  static shared_ptr<MustUseFactory> create();
};

You can still use make_shared by using aliasing:

shared_ptr<MustUseFactory> MustUseFactory::create()
{
    typedef aligned_storage<sizeof(MustUseFactory)> Storage;
    shared_ptr<Storage> storage = make_shared<Storage>();
    MustUseFactory* p = new (storage->address()) MustUseFactory();
    return shared_ptr<MustUseFactory>(storage, p);
}

This doesn't work with enable_shared_from_this, I leave that as an exercise for the reader ;-)

comment:4 by Peter Dimov, 14 years ago

Milestone: Boost 1.38.0Boost 1.39.0
Status: newassigned

comment:5 by Peter Dimov, 14 years ago

It still seems to me that the best course of action is to make allocate_shared use A::construct. Unfortunately, this doesn't work with C++03 allocators, whose construct method only takes a single T const& argument. C++0x allocators are OK, but changing boost::allocate_shared to call construct will make it fail for C++03 allocators.

comment:6 by Peter Dimov, 13 years ago

Milestone: Boost 1.39.0To Be Determined

comment:7 by Peter Dimov, 12 years ago

Status: assignednew

comment:8 by jwakely.boost@…, 11 years ago

Using std::allocator_traits<A>::construct will work with C++11 or C++03 allocators, which is what LWG 2070 suggests and what I've implemented for GCC 4.7

Of course that requires a C++11 library that provides allocator_traits

comment:9 by Peter Dimov, 9 years ago

Resolution: fixed
Status: newclosed

boost::allocate_shared now uses std::allocator_traits<>::construct and destroy (if available), as per LWG 2070.

Note: See TracTickets for help on using tickets.