2013-06-21 60 views
10

请注意,我正在使用C++ 03,并且delete d函数没有提供给我。防止编译器在C++中考虑隐式声明的拷贝构造函数03

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

请考虑我有两个主要对象:一个核心库对象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的复制构造函数,除非我自己声明至少一个。但即使有:

Fixture (*this) 

我希望编译器根本就没有考虑这些拷贝构造函数:

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

...编译器将仍然在Branch的初始化列表初始化Fixture时考虑这些private声明。

我可以用我自己做一个小的被扭曲做到这一点:

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

...但我宁愿不要,因为它是一个有点臭我的鼻子和非复制能力是语义我要去从boost::noncopyable推导Fixture

有没有一种方法,以防止考虑在这种情况下隐式声明的拷贝构造函数,编译器不会在调用点更改代码:

Fixture (*this) 


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

如果类定义不明确声明一个副本 构造函数,一个是隐式声明的。

+2

C++ 11中删除的函数甚至有帮助吗?删除的函数仍然参与重载解析。 –

+0

@KerrekSB:老实说,我不知道。如果你是对的,他们参与解决,那么我认为他们不会帮助。 –

+0

我相信[模板化构造函数](http://ideone.com/nHhzEi)应该明确优先... –

回答

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的重载是完全匹配的,比复制构造函数更好,所以它被明确地选为最好的过载。

+0

有了'enable_if',这个超载就不再适用于'Branch&'。也许'is_convertible '。 – aschepler

+0

我不禁想到SFINAE的魔法在这里有点过分。通过一个简单的'template'构造函数(非SFINAE),如果我传递了一些不可转换的东西(比如'FooBar'),它就会无法编译。 –

+0

这显然不会工作,因为'is_same'不会在这种情况下编译,正如我所说的,我正在使用C++ 03。我会解决它。 –

3

无法阻止复制构造函数签名的存在,不是在C++ 98中,也不在C++ 11中。 = delete也不会从超载设置中删除某些内容,它只会在选中时才会失败。

如果你不想混乱Fixture的公共接口,那么插入明确的转换就没有比这更好的想法了。

与接口混淆的选项包括通过指针传递Root以区别复制构造函数的参考和传递标记以便重载解析。如果你想知道更多关于这些的信息,请留下评论。

+0

+1我可以传递一个指针,但我宁愿传递一个引用,因为我将存储引用。如果我更改ctor来获取指针,那么我将不得不更改调用站点代码,所以我认为在这种情况下我更喜欢'static_cast'。 –

+0

顺便说一句,只要不改变'Branch'中的呼叫站点代码,我很高兴能够混淆'Fixture'的'public'接口。 –

2

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

对于这个问题,我会做BranchFixture实例作为它的成员,并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; 
};