views:

179

answers:

4

I have written a class with protected constructor, so that new instances can only be produced with a static create() function which returns shared_ptr's to my class. To provide efficient allocation I'd like to use boost::make_shared inside the create function, however the compiler complains that my class constructor is protected inside boost::make_shared. I decided to my boost::make_shared a friend of my class but I'm puzzled about the syntax. I tried

template< class T, class A1, class A2 >
friend boost::shared_ptr<Connection> boost::make_shared(const ConnectionManagerPtr&, const std::string&);

but the compiler gived me syntax errors. Please help.

+2  A: 

I would try without the template part. After all, you want a specific instantiation of the (template) function to be a friend of your class, aren't you? Does

friend boost::shared_ptr<Connection> boost::make_shared(const ConnectionManagerPtr&, const std::string&);

work?

If that's not the solution, it might be helpful to give us the compiler messages you're getting ...

MartinStettner
+1  A: 

I think that is not the right place to use make_shared. Just construct your object with operator new and pass the pointer to shared_ptr constructor. That way you don't need to be friends with anyone.

BTW, why template arguments and function arguments are of different type?

Basilevs
As there is no portable solution to the problem I choose this answer as accepted. BTW make_shared is preffered for performance reasons, because it requires one memory allocation per shared_ptr and gives better cache locality.
kyku
+3  A: 

You don't need to template the friend part, but you need to signify that the friend function is a template:

friend boost::shared_ptr<Connection> boost::make_shared<>(/* ... */);
//                                                     ^^

That works with Comeau and current GCC versions but fails with VC. Better would be the following form:

friend boost::shared_ptr<Connection> boost::make_shared<Connection>(/* ... */);

That works across multiple compilers now - i tested it on VC8, VC10, GCC 4.2, GCC 4.5 and Comeau 4.3.

Alternatively using a qualified name to refer to a particular instance of the function template as Martin does should work and does with Comeau, but GCC chokes on it.

A useful alternative that doesn't depend on the implementation details of make_shared() (and thus also works with VC10s TR1 implementation) is to use the pass-key-idiom for access-protection of the constructor and to befriend the create() function instead, e.g.:

class Connection {
// ...
public:
    class Key {
        friend boost::shared_ptr<Connection> create(const ConnectionManagerPtr&, 
                                                    const std::string&);
        Key() {}
    };
    Connection(const ConnectionManagerPtr&, const std::string&, const Key&);
};

boost::shared_ptr<Connection> create(const ConnectionManagerPtr& p, 
                                     const std::string& s) 
{
    return boost::make_shared<Connection>(p, s, Connection::Key());
}
Georg Fritzsche
I've just read Herb Sutter post on the subject and the outcome is that there is no portable solution to this problem. The code snippet you gave works on almost all compilers except GCC which I happen to develop on. So instead of using make_shared I reverted to using normal shared_ptr constructor.
kyku
Could you please post a link to that post?
Basilevs
@kyk: What versions? I tested that on GCC... Note that Sutters article is 7 years old.
Georg Fritzsche
@bas: [This one](http://www.drdobbs.com/184403853) from 2003 i guess.
Georg Fritzsche
Ok, the problem I was having was that I skipped constructor parameter type in friend declaration. So it shold be something likefriend boost::shared_ptr<Connection> boost::make_shared<Connection, std::string>(const std::string
kyku
A: 

Just a summary of how a complete version may look like:

#include <iostream>
#include <boost/make_shared.hpp>

class Foo {
  explicit Foo(int x) {
    std::cout << "Foo::Foo(" << x << ")\n";
  }
public:
  friend boost::shared_ptr<Foo> boost::make_shared<Foo, int>(const int& x);

  static boost::shared_ptr<Foo> create(int x) {
    return boost::make_shared<Foo, int>(x);
  }

  ~Foo() {
    std::cout << "Foo::~Foo()\n";
  }
};

int main(int argc, const char *argv[]) {
  Foo::create(42);
}
kyku