2016-07-27 61 views
2

下面是在VS2008中工作正常的地圖中存儲爲值的類的簡化版本(請注意,所有成員都是私有的):在std :: map中的C++ 11類與私有構造函數的值

class Value{ 
    friend class FriendClass; 
    friend class std::map<std::string, Value>; 
    friend struct std::pair<const std::string, Value>; 
    friend struct std::pair<std::string, Value>; 

    Value() {..} 
    Value(Value const& other) {..} 

    ... rest members... 
}; 

代碼(從FriendClass調用,所以這可以達到私有構造函數):

FriendClass::func() 
{ 
    std::map<const std::string, Value> map; 
    map.insert(std::make_pair(std::string("x"), Value())); 
} 

這將編譯的w/o在VS2008任何錯誤,但無法對VS2015/C++ 11:

 
file.cpp(178): error C2664: 'std::_Tree_iterator>>> std::_Tree>::insert(std::_Tree_const_iterator>>>,const std::pair &)': cannot convert argument 1 from 'std::pair' to 'std::pair &&' 
     with 
     [ 
      _Kty=std::string, 
      _Ty=Value, 
      _Pr=std::less, 
      _Alloc=std::allocator> 
     ] 
     and 
     [ 
      _Kty=std::string, 
      _Ty=Value 
     ] 
    file.cpp(178): note: Reason: cannot convert from 'std::pair' to 'std::pair' 
     with 
     [ 
      _Kty=std::string, 
      _Ty=Value 
     ] 
    file.cpp(178): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called 

如果我將Value拷貝構造函數設爲public,它也會在VS2015中編譯好。

但是這是私人的目的,並且只能用於std :: map和std :: pair。但是,似乎在C++ 11中還需要額外的朋友訪問權限來聲明。這些是哪些?

謝謝。

+0

如果我用'std :: string(「x」)'而不是普通的'char []''調用'make_pair',那麼你的代碼會在VS2015中編譯我的代碼: – Zereges

+0

@Zereges:很奇怪,因爲問題不同。即使我更改爲'std :: string(「x」)'我有同樣的錯誤。讓我改變例子以避免混淆。你確定你所有的構造函數都是私有的嗎? – Zotyi

回答

0

我無法訪問您提到的編譯器,但這裏是我在g ++ 5.3上看到的。

考慮你的問題的以下基本成相同的版本:

#include <map>                                
#include <utility> 


class foo 
{ 
    friend std::pair<const int, foo>; 

    foo(const foo &other){} 

public: 
    foo(){} 
}; 


int main() 
{ 
    using map_t = std::map<int, foo>; 

    map_t m; 
    m.insert(std::make_pair(2, foo())); 
    // m.emplace(2, foo()); 
} 

(默認構造函數是public,但是這非必要,只是使例子更短)

main,記兩線

m.insert(std::make_pair(2, foo())); 
// m.emplace(2, foo()); 

倒車的意見建立罰款,但顯示的版本不會:

/usr/include/c++/5/bits/stl_pair.h: In instantiation of ‘constexpr std::pair<_T1, _T2>::pair(_U1&&, const _T2&) [with _U1 = int; <template-parameter-2-2> = void; _T1 = int; _T2 = foo]’: 
/usr/include/c++/5/bits/stl_pair.h:281:72: required from ‘constexpr std::pair<typename std::__decay_and_strip<_Tp>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&) [with _T1 = int; _T2 = foo; typename std::__decay_and_strip<_T2>::__type = foo; typename std::__decay_and_strip<_Tp>::__type = int]’ 
stuff.cpp:21:34: required from here 
stuff.cpp:9:2: error: ‘foo::foo(const foo&)’ is private 
foo(const foo &other){} 
^ 

查看源代碼std_pair.h表明確實它試圖調用複製構造函數。不幸的是,你friend ed std::pair,但不是std::make_pair

emplace版本沒有這個問題,但我懷疑這是依賴於實現的。一般來說,如果你想讓一個容器存儲一個完全不透明的類,我建議你使用一個容器std::shared_ptr。這使您可以完全指定哪個函數/類可以在自己的代碼中創建/複製對象,並且不會對庫的代碼做任何假設。

+0

'不幸的是,你和std :: pair友好,但不是std :: make_pair.' - 我會朋友,但它沒有幫助(或者我做錯了)。我嘗試添加'朋友std :: pair std :: make_pair(std :: string &&,值爲&&);'但是同樣的錯誤。我沒有看到理由避免'emplace()'實際上,可能這將是更快的方式,但是對於其他解決方案(沒有shared_ptr/emplace/public構造函數)感到好奇,因爲它使用「舊」C++ w/o問題編譯。 – Zotyi

+0

然而,我試圖在我的答案中解釋爲什麼我認爲這不能幹乾淨淨地完成:你可以爲一個班級,一個功能或任何其他類型的朋友。但是,只要它不是你的代碼,你就不能確定它不會調用其他不是朋友的東西。因此,你所擁有的是在某些編譯器'emplace'的某些版本中恰好工作,並且你的舊版本沒有'make_pair'的重載,這是一個問題,等等.IIUC,沒有什麼禁止下一版本的'emplace'再次破壞你的代碼。 –

+0

所以'公共複製構造函數'或'shared_ptr'似乎是最好的解決方案。 – Zotyi