【发布时间】:2012-12-22 16:36:20
【问题描述】:
我的问题是关于 shared_ptr 的赋值运算符模板在 GCC 4.7.2 中的实现,我怀疑它包含一个错误。
前提 1:C++11 标准
这里是我说的赋值运算符模板的签名:
template<class Y> shared_ptr& operator=(const shared_ptr<Y>& r) noexcept;
来自 C++11 标准 (20.7.2.2.3):
“相当于 shared_ptr(r).swap(*this)。”
换句话说,赋值运算符模板是根据构造函数模板定义的。构造函数模板的签名如下:
template<class Y> shared_ptr(const shared_ptr<Y>& r) noexcept;
来自 C++11 标准 (20.7.2.2.1):
“要求:除非 Y* 可隐式转换为 T*,否则 [...] 构造函数不得参与重载决议。”
前提 2:GCC 4.7.2 的实现:
现在 GCC 4.7.2 的构造函数模板的实现对我来说似乎是正确的(std::__shared_ptr 是 std::shared_ptr 的基类):
template<typename _Tp1, typename =
typename std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
__shared_ptr(__shared_ptr<_Tp1, _Lp>&& __r) noexcept
:
_M_ptr(__r._M_ptr),
_M_refcount()
{
_M_refcount._M_swap(__r._M_refcount);
__r._M_ptr = 0;
}
但是,GCC 4.7.2 对赋值运算符模板的实现如下:
template<typename _Tp1>
__shared_ptr& operator=(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
{
_M_ptr = __r._M_ptr;
_M_refcount = __r._M_refcount; // __shared_count::op= doesn't throw
return *this;
}
让我印象深刻的是,这个操作不是根据构造函数模板定义的,也不是swap() 定义的。特别是,普通赋值 _M_ptr = __r._M_ptr 不会产生与通过 std::is_convertible 显式检查类型 _Tp1* 和 _Tp* 的可转换性时相同的结果(可以专门化)。
前提 3:VC10 实现
我注意到 VC10 在这方面确实有一个更符合标准的实现,我认为这是正确的,并且在我的测试用例中表现得符合我的预期(而 GCC 没有):
template<class _Ty2>
_Myt& operator=(const shared_ptr<_Ty2>& _Right)
{
// assign shared ownership of resource owned by _Right
shared_ptr(_Right).swap(*this);
return (*this);
}
问题:
GCC 4.7.2 对shared_ptr 的实现是否确实存在错误?我找不到任何关于这个问题的错误报告。
发布脚本:
如果你想问我我的测试用例是什么,为什么我会关心这个看似不重要的细节,为什么我似乎暗示我需要专攻std::is_convertible,请这样做在聊天中。这是一个很长的故事,没有办法在不被误解的情况下总结它(及其所有不愉快的后果)。提前谢谢你。
【问题讨论】:
-
我认为你在那里有错误的构造函数模板,你给出了从不同的原始指针转换的那个,而不是从不同的
shared_ptr转换的那个@ -
即使您可以专门化
is_convertible,但除非您保持标准要求的行为,否则不得这样做... -
[meta.type.synop]/1 “除非另有说明,否则为本子条款中定义的任何类模板添加特化的程序的行为是未定义的。”您是否有一个未定义的测试用例,并且对于 GCC 和 MSVC 的实现表现不同? IIRC 我直接从 Boost 复制了该赋值运算符代码。
-
我很好奇您的
is_convertible要求。请注意,该标准不需要std::is_convertible<T,U>::value,而是要求它们是可转换的,并且在赋值中隐式检查。提供is_convertible的特化只能限制使用,因为在测试通过的情况下仍需要进行初始化/分配。另请注意,该标准不要求使用 SFINAE 或使用std::is_convertible,因此根据该行为,您的程序不可移植。 -
@AndyProwl C++ 有一个通用的“好像”规则:如果位没有按照标准中的规定实现,但表现得“好像”它们是 - 意味着正确的程序无法判断它们'以不同的方式重新实现——这不是实现中的错误。这就是为什么您是否可以在已定义行为的程序中看到不同行为的问题非常相关。
标签: c++ gcc stl c++11 smart-pointers