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()
    );
}

但是,如果我将复制构造函数test从更改test(test const&) =delete;test(test const&) =default;,则模板错误呕吐物似乎抱怨const test&无法转换为(此处test为文本)。都不应该工作吗?或者如果没有,他们不应该都给出错误吗?

4

2 回答 2

12

如果您更仔细地查看模板错误呕吐物,您会在其中看到这块胡萝卜:

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- 如果替换导致无效的类型或表达式,则类型推导失败。无效类型或表达式是如果使用替换参数编写的格式错误的类型或表达式。[ 注意:访问检查是替换过程的一部分。——尾注]

这是对之前扣除规则的巨大改变,之前那段说

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

DR 1170在 C++0x 进程中进行了相当晚的更改,这使得 SFINAE 在 C++11 中非常棒:)

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

于 2013-02-12T16:18:33.637 回答
4

正确答案是乔纳森·韦克利的。我会留下这个,因为它为有类似问题的人提供了有用的信息insert


简短的版本是,这是由 GCC 4.7.2 使用的标准库实现中的问题引起的,这是由于 C++11 标准中使用的误导性措辞造成的。有一个对措辞的更改建议,以及对 GCC 4.8 中的实施的修复。


长版

这个 GCC 错误条目报告了一个非常相似的问题,insert使用 where 代替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 问题 #2005,请参阅Portland 2012条目,以下建议更改的相关部分):

  1. 将 23.5.4.4 [unord.map.modifers] 更改为 p。1 如图所示:

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

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

提议的更改还指出,上述insert功能的效果应等同于emplace(std::forward<P>(obj)). 因此,可以肯定地说,您的问题中描述的问题是完全相同的问题。

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

于 2013-02-11T09:50:22.337 回答