![](/img/trans.png)
[英]Using std::unique_ptr of a polymorphic class as key in std::unordered_map
[英]std::unordered_map<T,std::unique_ptr<U>> copyable? GCC bug?
g++ --version
產生:
g++.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 4.9.1
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
程序:
#include <memory>
#include <type_traits>
#include <unordered_map>
static_assert(!std::is_copy_constructible<std::unordered_map<int,std::unique_ptr<int>>>::value,"Copyable");
int main () { }
編譯結果:
.\unorderedmapcopyable.cpp:5:1: error: static assertion failed: Copyable
static_assert(!std::is_copy_constructible<std::unordered_map<int,std::unique_ptr<int>>>::value,"Copyable");
^
相關標准:
對於語句X u(a)
和X u=a
有效,對於某些包含類型T
容器類型X
,其中a
是類型X
的值:
要求:
T
是CopyInsertable
到X
§23.2.1[container.requirements.general]
我對此的理解:如果T
(在我們的例子中是std::pair<const int,std::unique_ptr<int>>
)不是CopyInsertable
到X
(在我們的例子中是std::unordered_map<int,std::unique_ptr<int>>
),然后X u(a)
和X u=a
的格式不正確。
CopyInsertable
T
是CopyInsertable
到X
意味着,除了T
是MoveInsertable
到X
,下面的表達式是格式良好的:
allocator_traits<A>::construct(m, p, v)
並且它的評估導致以下后置條件保持:
v
的值不變並等於*p
。
我對此的理解: std::pair<const int,std::unique_ptr<int>>
不是CopyInsertable
,因為std::unique_ptr<int>
不可復制:
從
CopyConstructible
條款[...]中指定的unique_ptr
模板實例化的類型U
每個對象不是CopyConstructible
也不是CopyAssignable
。§20.8.1[unique.ptr]
並且由於std::pair<const int,std::unique_ptr<int>>
的復制構造std::pair<const int,std::unique_ptr<int>>
是默認的:
pair(const pair&) = default;
§20.3.2[pairs.pair]
並且由於std::pair<const int,std::unique_ptr<int>>
的成員具有std::unique_ptr<int>
類型的成員:
template <class T1, class T2> struct pair {
[...]
T2 second;
§20.3.2[pairs.pair]
並且由於在不是類型的所有成員都是CopyConstructible
的情況下刪除了默認的復制構造函數:
如果X具有以下內容,則將類
X
默認復制/移動構造函數定義為已刪除:[...]
- 類型
M
(或其數組)的非靜態數據成員,由於應用於M
的相應構造函數的重載解析,無法復制/移動,導致[...]刪除的函數[...] ]§12.8[class.copy]
std::is_copy_constructible
對於可引用類型
T
,與is_constructible<T,const T&>::value
結果相同,否則為false
。§20.10.4.3[meta.unary.prop]
我對此的理解/解讀: std::is_copy_constructible<std::unordered_map<int,std::unique_ptr<int>>
與std::is_constructible<std::unordered_map<int,std::unique_ptr<int>,std::unordered_map<int,std::unique_ptr<int> &>
。
std::is_constructible
給出以下函數原型:
template <class T> add_rvalue_reference_t<T> create() noexcept;
當且僅當以下變量定義適用於某些發明變量
t
,才能滿足模板特化的謂詞條件is_constructible<T, Args...>
:
T t(create<Args>()...);
§20.10.4.3[meta.unary.prop]
我對此的理解: std::is_constructible<std::unordered_map<int,std::unique_ptr<int>>,std::unordered_map<int,std::unique_ptr<int> &>
應該是std::false_type
,不是std::true_type
,因為X u(a)
格式不正確。
是否應接受上述代碼? 這是一個GCC / libstdc ++錯誤,還是我缺少標准中的某些內容?
我目前無法訪問Clang或MSVC ++,否則我會對它們進行測試。
您的分析中存在兩個問題。
首先,違反Requires子句會導致未定義的行為(第17.6.4.11節[res.on.required]):
違反函數的Requires:段中指定的前提條件會導致未定義的行為,除非函數的Throws: paragraph指定在違反前提條件時拋出異常。
這意味着如果您嘗試使用非CopyInsertable元素復制構造unordered_map
,則庫可以執行任何操作。 它不一定會導致程序格式不正確(盡管它可能會在復制構造函數的實現中深處)。
其次, is_constructible
trait執行的測試僅限於直接上下文(§20.10.4.3[meta.unary.prop] / p7,強調添加):
執行訪問檢查就像在與
T
和任何Args
無關的上下文中一樣。 僅考慮變量初始化的直接上下文的有效性。 [ 注意 :初始化的評估可能會導致副作用,例如類模板特化和函數模板特化的實例化,隱式定義函數的生成等等。 這種副作用不在“直接背景”中,並且可能導致程序形成不良。 - 結束說明 ]
換句話說,這基本上只考慮是否存在匹配的,可訪問的和未刪除的構造函數簽名,而不是實例化構造函數將導致格式良好的代碼。
標准必須指定容器的復制構造函數,其中包含“如果T
不是CopyInsertable進入X
則此構造函數不應參與重載解析”以保證is_copy_constructible
特征的行為符合您的is_copy_constructible
。 標准中沒有這樣的規范。
正如Marc Glisse在評論中寫道,雖然這不是標准規定的,但它可以被認為是一個實施質量問題,因此錯誤報告是合理的。
編輯:我想到,從非CopyInsertable
元素的重載解析中刪除復制構造函數的要求可能無法實現,因為該屬性是根據對allocator_traits<A>::construct(m, p, v)
格式良好並具有所需的語義。 我不相信SFINAE可以確定對allocator_traits<A>::construct()
的調用主體的格式良好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.