10

请注意,我在 C++03 中工作,而deleteC++11 的 d 函数对我不可用。

我正在尝试设计一个不可复制的对象,并阻止编译器考虑该类上隐式声明的复制构造函数。这是我正在开发的单元测试夹具。

考虑一下我有两个主要对象:一个核心库对象Root和一个在测试中的派生特例对象Branch. 我正在尝试开发一个测试夹具类,Fixture它处理设置和与核心Root对象交谈的细节。所以这是我到目前为止所构建内容的简化说明:

这是一个ideone链接,下面有相同的代码,除了我定义了我自己的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&)

Fixture除非我自己声明至少一个,否则不可能*阻止 C++03 编译器隐式声明一个复制构造函数。但即使有:

class Fixture
:
    public boost::noncopyable
{
public:
    Fixture (Root& root)
    :
        mRoot (root)
    {
    }
private:
    Fixture (const Fixture&);
    Fixture (Fixture&);
    Root& mRoot;
};

...在的初始化列表中初始化时,编译器仍会考虑这些private声明:FixtureBranch

Fixture (*this)

我希望编译器根本不考虑这些复制构造函数。

我可以通过自己做一些扭曲来做到这一点:

Fixture (static_cast <Root&> (*this))

...但我宁愿不这样做,因为它对我的鼻子来说有点臭,而且不可复制能力Fixtureboost::noncopyable.

在这种情况下,有没有办法阻止编译器考虑隐式声明的复制构造函数,而无需更改调用站点的代码:

Fixture (*this)

?


  • “不可能……”:标准 C++03:12.8/4,“特殊成员函数”:

如果类定义没有显式声明复制构造函数,则隐式声明。

4

3 回答 3

4

您的歧义是*this可以绑定到 aRoot &和 a 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是完全匹配的,比复制构造函数更好,因此它被明确地选为最佳重载。

于 2013-06-21T17:39:10.577 回答
3

没有办法阻止复制构造函数签名的存在,不是在 C++98 中,也不是在 C++11 中。= delete也不会从重载集中删除某些东西,只有在被选中时才会失败。

如果您不想弄乱 Fixture 的公共接口,我没有比插入显式转换更好的主意。

与接口混淆的选项包括传递Rootby 指针以与复制构造函数的引用区分开来,并传递标记以解决重载问题。如果您想了解更多关于这些的信息,请发表评论。

于 2013-06-21T15:50:44.270 回答
2

如果您不打算Branch作为 的实例传递Fixture,则根本不需要继承它。如果我没记错的话,您基本上想要做的是能够Fixture为 的所有实例设置一些东西。Root所以让我们攻击那个原因,而不是弯曲 C++。免责声明:如果不是这样,恐怕我没有任何建议。

对于这个问题,我会通过将自身作为实例传递给' 的构造函数和赋值运算符来创建Branch实例,Fixture因为它的成员和重载复制构造函数的实例从不复制实例。一个简单的例子如下所示:BranchFixtureFixtureFixture

#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;
};
于 2013-06-21T16:25:12.057 回答