Note that I am working in C++03, and the delete
d functions of C++11 are not available to me.
I am attempting to design a non-copyable object, and prevent the compiler from considering the implicitly-declared copy constructor on that class. This is for a unit test fixture I am developing.
Consider that I have two main objects: a core library object, Root
, and a derived special-case object under test, Branch
. I am trying to develop a test fixture class, Fixture
which handles the minutiae of setting up & talking to the core Root
object. So this is a simplified illustration of what I've built so far:
(Here is an ideone link with the same code below, except I've defined my own noncopyable
)
#include <boost/utility.hpp>
#include <boost/noncopyable.hpp>
class Root
{
};
class Fixture
:
public boost::noncopyable
{
public:
Fixture (Root& root)
:
mRoot (root)
{
}
private:
Root& mRoot;
};
class Branch
:
public Root,
public Fixture
{
public:
Branch()
:
Fixture (*this)
{
}
};
int main()
{
Branch branch;
}
Compiling this results in:
main.cpp: In constructor ‘Branch::Branch()’:
main.cpp:30:23: error: call of overloaded ‘Fixture(Branch&)’ is ambiguous
main.cpp:30:23: note: candidates are:
main.cpp:13:5: note: Fixture::Fixture(Root&)
main.cpp:8:7: note: Fixture::Fixture(const Fixture&)
It is not possible* to prevent the C++03 compiler from implicitly declaring a copy constructor for Fixture
unless I declare at least one on my own. But even with:
class Fixture
:
public boost::noncopyable
{
public:
Fixture (Root& root)
:
mRoot (root)
{
}
private:
Fixture (const Fixture&);
Fixture (Fixture&);
Root& mRoot;
};
...the compiler will still consider these private
declarations when initializing Fixture
in Branch
's initialization list:
Fixture (*this)
I want the compiler to simply not consider these copy constructors.
I could do this by doing a little contorting on my own:
Fixture (static_cast <Root&> (*this))
...but I'd rather not, as it's a bit smelly to my nose and non-copy-ability is the semantics of what I'm going for by deriving Fixture
from boost::noncopyable
.
Is there a way to prevent the compiler from considering implicitly-declared copy constructors in this case without changing code at the call-site from:
Fixture (*this)
?
If the class definition does not explicitly declare a copy constructor, one is declared implicitly.
The copy constructor is used only for initializations, and does not apply to assignments where the assignment operator is used instead. The implicit copy constructor of a class calls base copy constructors and copies its members by means appropriate to their type. If it is a class type, the copy constructor is called.
In C++, a Copy Constructor may be called for the following cases: 1) When an object of the class is returned by value. 2) When an object of the class is passed (to a function) by value as an argument. 3) When an object is constructed based on another object of the same class.
Otherwise, the implicitly-declared copy constructor is T::T(T&).
The compiler also creates a copy constructor if we don't write our own copy constructor. Unlike the default constructor, the body of the copy constructor created by the compiler is not empty, it copies all data members of the passed object to the object which is being created.
Your ambiguity is that *this
can bind to both a Root &
and a Fixture &
, and both conversions are equally good (namely derived-to-base conversions).
The trick is to create an overload that's a better match. For example,
template <typename T> Fixture(T &)
is will match any lvalue exactly, and thus is a better match than an overload that requires a conversion.
However, this is too naive, since you don't actually want your Fixture
to be constructible from just anything. Rather you want it to be constructible only from something that's derived from Root
. We can disable the extraneous constructors with some SFINAE magic. First the C++11 version:
#include <type_traits>
template <typename T,
typename = typename std::enable_if<std::is_base_of<Root, T>::value>::type>
Fixture(T & x)
: mRoot(x)
{ }
In C++03, we use Boost, and we can't use default template arguments:
#include <boost/type_traits.hpp>
template <typename T>
Fixture(T & x,
typename boost::enable_if<boost::is_base_of<Root, T> >::type * = NULL)
: mRoot(x)
{ }
Now you are guaranteed that T
is derived from Root
. The overload of this templated constructor with T = Branch
is an exact match, better than the copy constructor, and so it is selected as the best overload unambiguously.
There is no way to prevent the existence of the copy constructor signature, not in C++98, and not in C++11 either. = delete
does not remove something from the overload set either, it only fails if it is selected.
I have no better idea than to insert the explicit cast if you don't want to mess with the public interface of Fixture.
Options that do mess with the interface include passing the Root
by pointer to distinguish from the reference of the copy constructor and passing a tag for the sake of overload resolution. Leave a comment if you want to know more about these.
If you're not going to pass around Branch
as an instance of Fixture
, there is no need for inheriting it at all. What you basically would like to do is be able to set something up in Fixture
for all instances of Root
, if I'm not mistaken. So lets attack that cause, instead of bending C++. DISCLAIMER: If it's otherwise, I've no suggestions, I'm afraid.
For this problem, I'd make Branch
have an instance of Fixture
as it's member and overload copy-constructor of Branch
to create the instance of Fixture
by passing itself as the instance to Fixture
's constructor and assignment operator to never copy Fixture
instance. A brief example is shown below:
#include <boost/utility.hpp>
#include <boost/noncopyable.hpp>
class Root
{
};
class Fixture
:
public boost::noncopyable
{
public:
Fixture (Root& root)
:
mRoot (root)
{
}
private:
Root& mRoot;
};
class Branch
:
public Root
{
public:
Branch()
: mFixture(*this)
{
}
Branch(const Branch& branch)
: Root(*this)
, mFixture(*this)
/* other 'Branch' members to be copied */
{
}
Branch& operator = (const Branch& branch)
{
Root::operator=(branch);
/* copy other 'Branch' members, except 'mFixture' */
}
Fixture& getFixture()
{
return mFixture;
}
const Fixture& getFixture() const
{
return mFixture;
}
private:
Fixture mFixture;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With