以下代码使用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)。不应该工作吗?或者,如果不是,他们不应该给出错误吗?
答案 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
代替emplace
。 insert
的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 条目,以下建议更改的相关部分) :
- 醇>
在p周围更改23.5.4.4 [unord.map.modifers]。 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
检查未执行且没有出现错误消息。