Tav*_*nes 39 c++ gcc libstdc++ argument-dependent-lookup c++11
以下片段:
#include <memory>
#include <utility>
namespace foo
{
template <typename T>
void swap(T& a, T& b)
{
T tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
}
struct bar { };
}
void baz()
{
std::unique_ptr<foo::bar> ptr;
ptr.reset();
}
Run Code Online (Sandbox Code Playgroud)
不为我编译:
$ g++ -std=c++11 -c foo.cpp
In file included from /usr/include/c++/5.3.0/memory:81:0,
from foo.cpp:1:
/usr/include/c++/5.3.0/bits/unique_ptr.h: In instantiation of ‘void std::unique_ptr<_Tp, _Dp>::reset(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = foo::bar; _Dp = std::default_delete<foo::bar>; std::unique_ptr<_Tp, _Dp>::pointer = foo::bar*]’:
foo.cpp:20:15: required from here
/usr/include/c++/5.3.0/bits/unique_ptr.h:342:6: error: call of overloaded ‘swap(foo::bar*&, foo::bar*&)’ is ambiguous
swap(std::get<0>(_M_t), __p);
^
In file included from /usr/include/c++/5.3.0/bits/stl_pair.h:59:0,
from /usr/include/c++/5.3.0/bits/stl_algobase.h:64,
from /usr/include/c++/5.3.0/memory:62,
from foo.cpp:1:
/usr/include/c++/5.3.0/bits/move.h:176:5: note: candidate: void std::swap(_Tp&, _Tp&) [with _Tp = foo::bar*]
swap(_Tp& __a, _Tp& __b)
^
foo.cpp:7:10: note: candidate: void foo::swap(T&, T&) [with T = foo::bar*]
void swap(T& a, T& b)
Run Code Online (Sandbox Code Playgroud)
这是我的错,因为声明一个swap()如此通用的函数会与它发生冲突std::swap吗?
如果是这样,有没有办法定义,foo::swap()以便它不被Koenig查找拖入?
dyp*_*dyp 25
unique_ptr<T>需要T*是NullablePointer[unique.ptr] p3NullablePointer需要的左值T*是Swappable[nullablepointer.requirements] P1Swappable本质上需要using std::swap; swap(x, y);选择一个重载x,y为T*[swappable.requirements] p3 类型的左值在最后一步中,您的类型foo::bar会产生歧义,因此违反了要求unique_ptr.libstdc ++的实现是顺应的,虽然我说这是相当令人惊讶的.
措辞当然有点复杂,因为它是通用的.
[unique.ptr] P3
如果类型
remove_reference_t<D>::pointer存在,那么unique_ptr<T, D>::pointer应该是同义词remove_reference_t<D>::pointer.否则unique_ptr<T, D>::pointer将是同义词T*.型号unique_ptr<T, D>::pointer应满足要求NullablePointer.
(强调我的)
[nullablepointer.requirements] P1
甲
NullablePointer型是支持空值的指针般类型.类型P符合以下要求NullablePointer:
- [...]
- 类型
P的左值是可交换的(17.6.3.2),- [...]
[swappable.requirements] P2
当且仅当以下情况时
t,对象u才可与对象交换:
- 表达式
swap(t, u)和swap(u, t)在下面描述的上下文中计算时是有效的,并- [...]
[swappable.requirements] P3
其中
swap(t, u)和swap(u, t)被评估的上下文应确保通过候选集上的重载决策选择名为"swap"的二进制非成员函数,该候选集包括:
swap在<utility>和中定义的两个函数模板- 由参数依赖查找生成的查找集.
请注意,对于指针类型T*,出于ADL的目的,关联的命名空间和类是从类型派生的T.因此,foo::bar*具有foo关联的命名空间.ADL的swap(x, y)情形之一x或者y是foo::bar*将因此找到foo::swap.
小智 14
问题是libstdc ++的实现unique_ptr.这是他们的4.9.2分支:
https://gcc.gnu.org/onlinedocs/gcc-4.9.2/libstdc++/api/a01298_source.html#l00339
338 void
339 reset(pointer __p = pointer()) noexcept
340 {
341 using std::swap;
342 swap(std::get<0>(_M_t), __p);
343 if (__p != pointer())
344 get_deleter()(__p);
345 }
Run Code Online (Sandbox Code Playgroud)
如您所见,有一个不合格的交换调用.现在让我们看看libcxx(libc ++)的实现:
_LIBCPP_INLINE_VISIBILITY void reset(pointer __p = pointer()) _NOEXCEPT
{
pointer __tmp = __ptr_.first();
__ptr_.first() = __p;
if (__tmp)
__ptr_.second()(__tmp);
}
_LIBCPP_INLINE_VISIBILITY void swap(unique_ptr& __u) _NOEXCEPT
{__ptr_.swap(__u.__ptr_);}
Run Code Online (Sandbox Code Playgroud)
他们不会swap在内部打电话,reset也不会使用不合格的掉期电话.
Dyp的答案提供了一个非常可靠的细分,说明了为什么libstdc++符合要求,以及为什么只要swap标准库需要调用你的代码就会中断.引用TemplateRex:
您没有理由
swap在仅包含特定类型的特定命名空间中定义此类通用模板.只需swap为其定义非模板重载foo::bar.保持一般交换std::swap,并仅提供特定的重载.资源
作为一个例子,这将无法编译:
std::vector<foo::bar> v;
std::vector<foo::bar>().swap(v);
Run Code Online (Sandbox Code Playgroud)
如果您的目标是使用旧标准库/ GCC(如CentOS)的平台,我建议使用Boost而不是重新发明轮子以避免像这样的陷阱.
Tav*_*nes 12
此技术可用于避免foo::swap()ADL找到:
namespace foo
{
namespace adl_barrier
{
template <typename T>
void swap(T& a, T& b)
{
T tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
}
}
using namespace adl_barrier;
}
Run Code Online (Sandbox Code Playgroud)
这就是Boost.Range的独立begin()/ end()功能的定义方式.在问这个问题之前我尝试了类似的东西,但确实做了,但是没有using adl_barrier::swap;用.
至于问题中的片段是否应该按原样运作,我不确定.我可以看到的一个复杂因素是unique_ptr可以使用自定义pointer类型Deleter,它应该与通常的using std::swap; swap(a, b);习惯用法交换.foo::bar*在这个问题中,这个习语明显被打破了.
| 归档时间: |
|
| 查看次数: |
2112 次 |
| 最近记录: |