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

use*_*267 10 c++ gcc unordered-map mingw c++11

使用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()
    );
}
Run Code Online (Sandbox Code Playgroud)

如果我改变了拷贝构造函数testtest(test const&) =delete;test(test const&) =default;然而,模板错误呕吐,似乎抱怨const test&不被转换为test(文字在这里).不应该工作吗?或者如果没有,他们是否应该给出错误?

Jon*_*ely 11

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

test.exe.cpp:8:3: error: 'constexpr test::test(const test&)' is private
Run Code Online (Sandbox Code Playgroud)

这是问题的线索.

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

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

这是对先前扣除规则的巨大改变,此前段落说

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

DR 1170在C++ 0x过程中进行了很晚的改变,并使得SFINAE在C++ 11中非常出色:)

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