请注意,我在C ++ 03中工作,而且我无法使用C ++ 11的delete
d函数。
我正在尝试设计一个不可复制的对象,并阻止编译器考虑该类上隐式声明的复制构造函数。这是我正在开发的单元测试夹具。
考虑到我有两个主要对象:核心库对象Root
和正在测试的派生特殊情况对象Branch
。我正在尝试开发一个测试夹具类Fixture
来处理设置和安装的细节。与核心Root
对象交谈。所以这是我迄今为止所构建内容的简化说明:
(Here is an ideone link下面的代码相同,只是我已经定义了自己的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;
}
编译结果为:
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&)
不可能*阻止C ++ 03编译器隐式声明Fixture
的复制构造函数,除非我自己声明至少一个。但即便如此:
class Fixture
:
public boost::noncopyable
{
public:
Fixture (Root& root)
:
mRoot (root)
{
}
private:
Fixture (const Fixture&);
Fixture (Fixture&);
Root& mRoot;
};
...初始化private
初始化列表中的Fixture
时,编译器仍会考虑这些Branch
声明:
Fixture (*this)
我希望编译器不要考虑这些复制构造函数。
我可以通过自己做一点扭曲来做到这一点:
Fixture (static_cast <Root&> (*this))
...但我不愿意,因为我的鼻子有点臭,非复制能力是语义我想通过派生{{1}来自Fixture
。
有没有办法阻止编译器在这种情况下考虑隐式声明的复制构造函数而不更改调用站点的代码:
boost::noncopyable
如果类定义未明确声明副本 构造函数,一个是隐式声明的。
答案 0 :(得分:4)
您的歧义是*this
可以绑定到Root &
和Fixture &
,两种转换同样都很好(即派生到基础转换)。
诀窍是创建一个更好匹配的重载。例如,
template <typename T> Fixture(T &)
将匹配任何左值正好,因此比需要转换的重载更好。
然而,这太天真了,因为你实际上并不希望你的Fixture
可以从任何东西构建。相反,你希望它只能从Root
派生的东西构造出来。我们可以通过一些SFINAE魔法来禁用无关的构造函数。首先是C ++ 11版本:
#include <type_traits>
template <typename T,
typename = typename std::enable_if<std::is_base_of<Root, T>::value>::type>
Fixture(T & x)
: mRoot(x)
{ }
在C ++ 03中,我们使用Boost,我们不能使用默认模板参数:
#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)
{ }
现在可以保证T
来自Root
。这个带有T = Branch
的模板化构造函数的重载是精确匹配,比复制构造函数更好,因此它被明确地选为最佳重载。
答案 1 :(得分:3)
没有办法阻止复制构造函数签名的存在,不是在C ++ 98中,也不是在C ++ 11中。 = delete
也不会从重载集中删除某些内容,只有在选中时才会失败。
如果你不想搞乱Fixture的公共接口,我没有比插入显式强制转换更好的想法。
混乱接口的选项包括通过指针传递Root
以区分复制构造函数的引用并为了重载解析而传递标记。如果您想了解更多相关信息,请发表评论。
答案 2 :(得分:2)
如果您不打算将Branch
作为Fixture
的实例传递,则根本不需要继承它。如果我没有弄错的话,你基本上想做的是能够在Fixture
中为Root
的所有实例设置一些内容。所以让攻击导致,而不是弯曲C ++。 免责声明:如果不是这样,我没有建议,我很害怕。
对于这个问题,我要让Branch
有一个Fixture
的实例作为其成员,并重载Branch
的复制构造函数来创建Fixture
的实例将自身作为实例传递给Fixture
的构造函数和赋值运算符,从不复制Fixture
实例。一个简短的例子如下所示:
#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;
};