私有/删除拷贝构造函数的std :: unordered_map :: emplace问题

时间:2013-02-11 08:51:17

标签: c++ gcc c++11 mingw unordered-map

以下代码使用gcc 4.7.2(mingw)

进行编译
#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()
    );
}

如果我将test中的复制构造函数从test(test const&) =delete;更改为test(test const&) =default;,则模板错误呕吐似乎会抱怨const test&无法转换为test }(文字here)。不应该工作吗?或者,如果不是,他们不应该给出错误吗?

2 个答案:

答案 0 :(得分:11)

如果你仔细看看模板错误呕吐,你会看到这块胡萝卜:

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

这是问题的线索。

GCC 4.7.2不会将访问检查作为模板参数推导的一部分(如C ++ 03所要求的那样)。is_convertible特征是使用SFINAE实现的,它依赖于模板参数推导,以及如果重载决策选择私有构造函数参数推导成功,但访问检查失败,因为所选构造函数是私有的。这是GCC 4.7的一个问题,因为它没有被改为遵循14.8.2 [temp.deduct]中新的C ++ 11规则,该规则说:

  

-8-如果替换导致无效的类型或表达式,则类型推导失败。如果使用替换参数写入,则无效的类型或表达式将是格式错误的。 [注意:访问检查是替换过程的一部分。 -end note]

这是先前扣除规则的一个巨大变化,之前该段说

  

-8-如果替换导致无效的类型或表达式,则类型推导失败。如果使用替换参数写入,则无效的类型或表达式将是格式错误的。访问检查不是替换过程的一部分。因此,当推导成功时,在实例化函数时仍可能导致访问错误。

在{+ 3}}的C ++ 0x过程中进行了很晚的更改,并使得SFINAE在C ++ 11中非常棒:)

GCC 4.8实现了新规则,因此is_convertible和类似的特征为不可访问的构造函数提供了正确的答案。

答案 1 :(得分:4)

正确答案是Jonathan Wakeley的。我会留下这个,因为它为那些与insert有相似问题的人提供了有用的信息。


简短版本是由于GCC 4.7.2使用的标准库实现中的问题引起的,这是由于C ++ 11标准中使用的误导性措辞造成的。有关措辞的变更提案,以及GCC 4.8中的实施方案。


长版

This GCC bug entry报告了一个非常类似的问题,其中insert代替emplaceinsert的libstdc ++实现遵循标准,其中说明了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,请参阅 Portland 2012 条目,以下建议更改的相关部分) :

  
      
  1. 在p周围更改23.5.4.4 [unord.map.modifers]。 1如所示:

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

    [...]   备注:此签名不应参与重载决策,除非P 可隐式转换为value_type std::is_constructible<value_type, P&&>::value为真。

建议的更改还指出上述insert函数的效果应与emplace(std::forward<P>(obj))的效果相同。因此,可以肯定地说,问题中描述的问题是完全相同的问题。

事实上,提议的更改似乎反映在最近的 GCC 4.8快照中:当您使用GCC 4.8编译代码时,is_convertible检查未执行且没有出现错误消息。