2013-02-11 67 views
10

如果我從test(test const&) =delete;改變test拷貝構造函數test(test const&) =default;但下面的代碼編譯罰款與GCC 4.7.2(MinGW的)的std :: unordered_map ::佈設問題與私人/刪除拷貝構造函數

#include <unordered_map> 
#include <tuple> 

struct test 
{ 
     test() =default; 
    private: 
     test(test const&) =delete; 
}; 

int main() 
{ 
    std::unordered_map<char, test> map; 

    map.emplace(
     std::piecewise_construct, 
     std::forward_as_tuple('a'), 
     std::forward_as_tuple() 
    ); 
} 

,模板錯誤嘔吐似乎抱怨const test&不可轉換爲test(文本here)。不應該工作?或者,如果不是,他們是不是應該給出一個錯誤?

回答

11

如果您在模板錯誤嘔吐謹慎看多,你會看到在它的胡蘿蔔這個塊:

test.exe.cpp:8:3: error: 'constexpr test::test(const test&)' is private 

這就是線索的問題。

GCC 4.7.2不會將訪問檢查作爲模板參數推導的一部分(如C++ 03所要求的那樣)。使用SFINAE實現is_convertible特徵,SFINAE依賴於模板參數推導,並且如果重載分辨率選擇一個私有構造函數參數演繹成功,但隨後訪問檢查失敗,因爲選定的構造函數是私有的。這是GCC 4.7的一個問題,因爲它沒有被更改爲遵循14.8.2 [temp.deduct]中的新C++ 11規則,該規則說:

-8-如果替換導致無效的類型或表達式,類型扣除失敗。無效的類型或表達式是使用替代參數編寫的格式不正確的類型或表達式。 [注意:訪問檢查是作爲替代過程的一部分完成的。 末端注]

這是以前的演繹規則一個巨大的變化,以前的那款說

-8-如果一個無效的類型或表達式替換結果,類型推導失敗。無效的類型或表達式是使用替代參數編寫的格式不正確的類型或表達式。訪問檢查不是作爲替代過程的一部分來完成的。因此,扣除成功後,實例化功能時仍可能導致訪問錯誤。

做出更改在C++ 0x中的過程比較晚的DR 1170,使SFINAE在C++ 11 :)

GCC完全真棒4.8實現了新的規則,因此is_convertible和相似的特徵爲無法訪問的構造函數提供正確的答案。

4

正確答案是Jonathan Wakeley's。我會離開這個,因爲它爲有類似insert相關問題的人提供有用的信息。


短的版本是,這是通過GCC 4.7.2應用於標準庫實現的問題,導致從在C++ 11標準中使用的措辭誤導引起的。有關措辭的更改建議,以及GCC 4.8中的實施修正。


龍版

This GCC bug entry報告一個非常類似的問題,其中insert代替emplace。所述的libstdC++實現的insert如下標準,其中指出關於insert函數(具體地,template <class P> pair<iterator,bool> insert(P&& obj)):

(§23.5.4.4/ 5)說明:該簽名不得參加重載除非P是隱式轉換到value_type

的libstdC++似乎已經實現了使用enable_if聲明檢查std::is_convertible<>所涉及的類型的這一要求。

上面鏈接的錯誤報告表明應該使用真正的std::is_constructible<>,並且應該更改標準中的措辭。它鏈接到LWG(語言工作組)的問題,這提出了改變標準這個已經(LWG issue #2005,見波特蘭2012項,下面的建議更改的相關部分):

  1. 更改23.5.4.4 [unord.map.modifers]圍繞p。 1所示:

    template <class P> 
    pair<iterator, bool> insert(P&& obj); 
    

[...] 備註:此簽名不得參與重載解析,除非P爲 隱式轉換爲VALUE_TYPE std::is_constructible<value_type, P&&>::value是真實的。

建議的更改還說明上述insert函數的作用應該等於emplace(std::forward<P>(obj))的作用。因此,可以說你的問題中描述的問題完全是同一個問題。

事實上,所提出的變化似乎反映在最近GCC 4.8快照:當您與GCC 4.8編譯代碼,不執行is_convertible檢查,並出現沒有錯誤消息。

+1

關閉,但沒有雪茄:) LWG 2005只適用於'insert'而非'emplace',程序因GCC 4.7.2失敗的原因是它不會執行訪問檢查作爲模板參數推演的一部分在C++ 03中是必需的),所以'is_constructible'由於私有構造函數而導致訪問失敗。 GCC 4.8實現C++ 11規則並在模板參數推導期間檢查訪問 – 2013-02-12 16:10:38

+2

您可以通過使用G ++ 4.7預處理代碼(因此它使用4.7中的庫)來確認差異不是由於標準庫中的任何更改而導致的,然後編譯它與4.8,在這種情況下,程序工作,證明它是編譯器而不是庫改變 – 2013-02-12 16:20:27