请注意,我在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))
...但是我不愿意,因为我的 Nose 有点臭,不可复制性是我从
Fixture
导出boost::noncopyable
所要实现的语义。有没有一种方法可以防止编译器在这种情况下考虑隐式声明的副本构造函数,而无需从以下位置更改调用站点的代码:
Fixture (*this)
?
最佳答案
您的歧义是*this
可以同时绑定(bind)到Root &
和Fixture &
,并且两种转换都同样好(即派生到基本的转换)。
诀窍是创建一个更好匹配的重载。例如,
template <typename T> Fixture(T &)
is将完全匹配任何左值,因此比需要转换的重载更好。
但是,这太天真了,因为您实际上不希望
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
的此模板化构造函数的重载是完全匹配的,优于复制构造函数,因此毫不含糊地将其选择为最佳重载。