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
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 ...
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?
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());
}
I ended up using the below simple solution to enforce shared ownership. No friendship required.
class probe {
probe() = default;
probe(...) { ... }
// Part I of III, private
struct creation_token {};
probe(probe const&) = delete;
probe& operator=(probe const&) = delete;
public:
// Part II of III, public
template <class... Args>
probe(creation_token&&, Args&&... args):
probe(std::forward<Args>(args)...) {}
// Part III of III, public
template <class... Args>
static auto create(Args&&... args) {
return make_shared<probe>(creation_token(),
std::forward<Args>(args)...);
}
};
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);
}
Below are some macros I wrote up to do this for you. In your case, you would use:
BOOST_MAKE_SHARED_2ARG_CONSTRUCTOR(Connection, const ConnectionManagerPtr&, const std::string&);
Macro definitions:
// Required includes
#include <boost/make_shared.hpp>
#include <boost/type_traits/add_reference.hpp>
#include <boost/type_traits/add_const.hpp>
// Helper macro
#define CONST_REFERENCE(T) boost::add_reference<boost::add_const<T>::type>::type
/** BOOST_MAKE_SHARED_nARG_CONSTRUCTOR(CLASS_NAME, ARG1_TYPE, ARG2_TYPE, ...)
*
* Use this macro inside the body of a class to declare that boost::make_shared
* should be considered a friend function when used in conjunction with the
* constructor that takes the given argument types. This allows the constructor
* to be declared private (making it impossible to accidentally create an instance
* of the object without immediatly storing it in a boost::shared_ptr).
* Example usage:
*
* class Foo {
* private:
* Foo(int size, const char* name);
* MAKE_SHARED_2ARG_CONSTRUCTOR(Foo, int, const char*);
* };
*
* boost::shared_ptr<Foo> myFoo = boost::make_shared<Foo>(3, "Bob");
*
* Note that you need to explicitly specify the number of arguments
* that the constructor takes as part of the macro name. Also, note that
* macros don't mix well with templated types that contain commas -- so
* if you have such a type, then you should typedef it to a shorter name
* before using it with this macro.
*/
#define BOOST_MAKE_SHARED_0ARG_CONSTRUCTOR(CLASS_NAME) \
friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>()
#define BOOST_MAKE_SHARED_1ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1) \
friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1))
#define BOOST_MAKE_SHARED_2ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2) \
friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2))
#define BOOST_MAKE_SHARED_3ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3) \
friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3))
#define BOOST_MAKE_SHARED_4ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4) \
friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4))
#define BOOST_MAKE_SHARED_5ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4, ARG_TYPE5) \
friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4), CONST_REFERENCE(ARG_TYPE5))
#define BOOST_MAKE_SHARED_6ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4, ARG_TYPE5, ARG_TYPE6) \
friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4), CONST_REFERENCE(ARG_TYPE5), CONST_REFERENCE(ARG_TYPE6))
#define BOOST_MAKE_SHARED_7ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4, ARG_TYPE5, ARG_TYPE6, ARG_TYPE7) \
friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4), CONST_REFERENCE(ARG_TYPE5), CONST_REFERENCE(ARG_TYPE6)), CONST_REFERENCE(ARG_TYPE7))
#define BOOST_MAKE_SHARED_8ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4, ARG_TYPE5, ARG_TYPE6, ARG_TYPE7, ARG_TYPE8) \
friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4), CONST_REFERENCE(ARG_TYPE5), CONST_REFERENCE(ARG_TYPE6)), CONST_REFERENCE(ARG_TYPE7)), CONST_REFERENCE(ARG_TYPE8))
#define BOOST_MAKE_SHARED_9ARG_CONSTRUCTOR(CLASS_NAME, ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, ARG_TYPE4, ARG_TYPE5, ARG_TYPE6, ARG_TYPE7, ARG_TYPE8, ARG_TYPE9) \
friend boost::shared_ptr<CLASS_NAME> boost::make_shared<CLASS_NAME>(CONST_REFERENCE(ARG_TYPE1), CONST_REFERENCE(ARG_TYPE2), CONST_REFERENCE(ARG_TYPE3), CONST_REFERENCE(ARG_TYPE4), CONST_REFERENCE(ARG_TYPE5), CONST_REFERENCE(ARG_TYPE6)), CONST_REFERENCE(ARG_TYPE7)), CONST_REFERENCE(ARG_TYPE8)), CONST_REFERENCE(ARG_TYPE9))