
Does inheriting from boost::noncopyable has the same effect like making the copy c'tor and the assignment operator private without implementing them? The authors of the smart_ptr library suggest the following for the use of the smart pointers with interfaces: class Interface { public: virtual void f() = 0; virtual void g() = 0; protected: ~Interface( void ) {}; // non-virtual } // #1 class Implementation : public Interface { public: void f() { /* Implementation */ } void g() { /* Implementation */ } private: // Not to be implemented Implementation( const Implementation& ); Implementation& operator=( const Implementation& ); } Does inheriting from boost::noncopyable has the same effect: // # 2 class Implementation : public Interface, /*public or private*/ boost::noncopyable { public: void f() { /* Implementation */ } void g() { /* Implementation */ } } 1.) Are #1 and #2 equivalent? 2.) When should I derive public and when private from boost::noncopyable? 3.) Is the use of noncopyable correct in this context and what are other scenarios when to use it? Regards, -Dirk

Dirk Gregorius <dirk <at> dirkgregorius.de> writes:
Does inheriting from boost::noncopyable has the same effect like making the copy c'tor and the assignment operator private without implementing them? The authors of the smart_ptr library suggest the following for the use of the smart pointers with interfaces:
class Interface { public: virtual void f() = 0; virtual void g() = 0;
protected: ~Interface( void ) {}; // non-virtual }
// #1 class Implementation : public Interface { public: void f() { /* Implementation */ } void g() { /* Implementation */ }
private: // Not to be implemented Implementation( const Implementation& ); Implementation& operator=( const Implementation& ); }
Does inheriting from boost::noncopyable has the same effect:
// # 2 class Implementation : public Interface, /*public or private*/ boost::noncopyable { public: void f() { /* Implementation */ } void g() { /* Implementation */ } }
1.) Are #1 and #2 equivalent?
In a broad sense, yes. The problem is that many compilers don't do EBO (empty base optiimzation) in the presence of multiple inheritance, which might end up (in #2) with sizeof(Implementation) being greater than strictly needed.
2.) When should I derive public and when private from boost::noncopyable?
Given what I said, I think that deriving from boost::noncpyable must be avoided if multiple inheritance is present and you expct your class to be allocated on the stack. In all other cases, deriving from noncpyable is more expressive than the traditional idiom. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

Joaquin M Lopez Munoz wrote:
Dirk Gregorius <dirk <at> dirkgregorius.de> writes:
1.) Are #1 and #2 equivalent?
In a broad sense, yes. The problem is that many compilers don't do EBO (empty base optiimzation) in the presence of multiple inheritance, which might end up (in #2) with sizeof(Implementation) being greater than strictly needed.
Correct.
2.) When should I derive public and when private from boost::noncopyable?
Given what I said, I think that deriving from boost::noncpyable must be avoided if multiple inheritance is present and you expct your class to be allocated on the stack.
"Must be avoided" is a bit strong. One extra byte usually makes no difference for a class that will be allocated on the stack. If you're going to have hundreds of thousands of them in an application, then maybe it's worth thinking about. And anyway, this limitation only applies to some compilers. If the OP is targeting GCC 3.x, for example, he doesn't need to worry about that EBO pessimization at all. Anyway the OP asked about public vs. private. It doesn't really matter which you use, though for maximum style points you should use private inheritance with noncopyable.
In all other cases, deriving from noncpyable is more expressive than the traditional idiom.
It's always more expressive. Sometimes there are good reasons to sacrifice that expressivity to achieve something else. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Dirk Gregorius wrote:
Does inheriting from boost::noncopyable has the same effect like making the copy c'tor and the assignment operator private without implementing them? The authors of the smart_ptr library suggest the following for the use of the smart pointers with interfaces:
class Interface { public: virtual void f() = 0; virtual void g() = 0;
protected: ~Interface( void ) {}; // non-virtual }
// #1 class Implementation : public Interface { public: void f() { /* Implementation */ } void g() { /* Implementation */ }
private: // Not to be implemented Implementation( const Implementation& ); Implementation& operator=( const Implementation& ); };
Does it really make any sense making only implementation noncopyable, rather than interface? If you deal with interfaces only it won't save you from errors like this: std::auto_ptr<Interface> create() { return std::auto_ptr<Interface>(new Implementation); } void foo() { std::auto_ptr<Interface> a(create()), b(create()); *a = *b; // oops, no error here, Implementation::operator= is not considered } -- Maxim Yegorushkin

Does it really make any sense making only implementation noncopyable, rather than interface? If you deal with interfaces only it won't save you from errors like this:
std::auto_ptr<Interface> create() { return std::auto_ptr<Interface>(new Implementation); }
void foo() { std::auto_ptr<Interface> a(create()), b(create());
*a = *b; // oops, no error here, Implementation::operator= is not considered }
I saw some code that declared the copy operations private in the base class. class Interface { public: virtual void f() = 0; private: Interface( const Interface& ); Interface& operator=( const Interface& ); } So in order to gain maximum style and correctness points I need to make both classes ( Interface and Implementation ) noncopyable? Which means both need to derive from boost::noncopyable? BTW: Why is this possible? Does it make sense to dereference an interface? Interface* p1 = make_interface(); Interface* p2 = make_interface(); *p1 = *p2; Or does your example only work with auto/smart pointers?

Dirk Gregorius wrote: []
I saw some code that declared the copy operations private in the base class.
class Interface { public: virtual void f() = 0;
private: Interface( const Interface& ); Interface& operator=( const Interface& ); }
So in order to gain maximum style and correctness points I need to make both classes ( Interface and Implementation ) noncopyable?
Only a base class. This prevents a compiler from generating copy ctor and assignment in its successors.
Why is this possible?
Because a compiler generates copy ctor and assignment when nothing prevents it.
Does it make sense to dereference an interface?
It does make sense, since there can be functions taking an interface by reference. But dereferencing and assigning it does not.
Interface* p1 = make_interface(); Interface* p2 = make_interface();
*p1 = *p2;
Or does your example only work with auto/smart pointers?
For plain pointers also. Some say noncopyable interfaces may save from errors, though making them noncopyable I've never encountered that kind of errors. -- Maxim Yegorushkin

On 12/19/04 7:43 AM, "Dirk Gregorius" <dirk@dirkgregorius.de> wrote:
Does it really make any sense making only implementation noncopyable, rather than interface? If you deal with interfaces only it won't save you from errors like this:
std::auto_ptr<Interface> create() { return std::auto_ptr<Interface>(new Implementation); }
void foo() { std::auto_ptr<Interface> a(create()), b(create());
*a = *b; // oops, no error here, Implementation::operator= // is not considered }
I saw some code that declared the copy operations private in the base class.
class Interface { public: virtual void f() = 0;
private: Interface( const Interface& ); Interface& operator=( const Interface& ); }
So in order to gain maximum style and correctness points I need to make both classes ( Interface and Implementation ) noncopyable? Which means both need to derive from boost::noncopyable?
I think that this is a bad idea. If "Interface" has no data members, then it's better to leave the copying operations unspecified. Manually inserting copy-blocking declarations does more harm than good. It does not provide a ban on derived classes ever getting copy operations, since the derived class's copy constructor can use _any_ base class constructor and the derived class's assignment operator doesn't have to call the base class's assignment operator. (A derived class can't have automatically defined copy operations, though; the evil copying operations have to be explicitly written.) Worse, the "Interface" class as given has no usable constructors, because the private & undefined copy constructor STILL cancels the automatically defined default constructor! Granted, the presence of a pure virtual member function already bans creation of "Interface" objects, but no derived classes can be made either. As I implied, let the automatically defined copy operations for abstract classes work. This applies to abstract classes with no data members that have problems automatically managing their resources (includes classes with no data members at all). For members that need explicit copying management, like shared pointer class internals, then provide protected versions of the standard copying operations. Ban the copying operations only if there is no way to create sane copying semantics.
BTW:
Why is this possible? Does it make sense to dereference an interface?
No, it doesn't make sense for what you wrote next. It's possible because C++ in general (along with C) does not censor what you do with return values.
Interface* p1 = make_interface(); Interface* p2 = make_interface();
*p1 = *p2;
Unless the "Interface" assignment operator is virtual, this will do slicing! That will be very bad in general. Note that automatically defined operations are never virtual. In the case here, making a protected assignment operator may help by making the compiler spit out an access error (unless this code is within a derived class's method).
Or does your example only work with auto/smart pointers?
-- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

Maxim Yegorushkin wrote:
Does it really make any sense making only implementation noncopyable, rather than interface?
This would prevent you from making a copyable implementation of the interface.
If you deal with interfaces only it won't save you from errors like this:
std::auto_ptr<Interface> create() { return std::auto_ptr<Interface>(new Implementation); }
void foo() { std::auto_ptr<Interface> a(create()), b(create());
Assuming you mean boost::shared_ptr here. auto_ptr doesn't work.
*a = *b; // oops, no error here, Implementation::operator= is not considered
This is (1) a no-op, nothing dangerous happens; (2) pretty rare, in my experience. If you really want to catch that, you can make the assignment operator of Interface protected.
}

Peter Dimov wrote:
Maxim Yegorushkin wrote:
Does it really make any sense making only implementation noncopyable, rather than interface?
This would prevent you from making a copyable implementation of the interface.
No. Deriving from boost::noncopyable only suppress compiler generated copy ctor and assignment. You still can define them and make your boost::noncopyable successor copyable. I think I just did not get you right.
If you deal with interfaces only it won't save you from errors like this:
std::auto_ptr<Interface> create() { return std::auto_ptr<Interface>(new Implementation); }
void foo() { std::auto_ptr<Interface> a(create()), b(create());
Assuming you mean boost::shared_ptr here. auto_ptr doesn't work.
No, I meant std::auto_ptr<>. I overlooked the protected destructor :). -- Maxim Yegorushkin

Maxim Yegorushkin wrote:
Peter Dimov wrote:
Maxim Yegorushkin wrote:
Does it really make any sense making only implementation noncopyable, rather than interface?
This would prevent you from making a copyable implementation of the interface.
No. Deriving from boost::noncopyable only suppress compiler generated copy ctor and assignment. You still can define them and make your boost::noncopyable successor copyable.
Yes, you are right. It doesn't prevent copyable implementations. Not an idiom that I would use, personally, but it is legitimate.
participants (6)
-
Daryle Walker
-
David Abrahams
-
Dirk Gregorius
-
Joaquin M Lopez Munoz
-
Maxim Yegorushkin
-
Peter Dimov