2013-06-21 62 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; 
};