Vit*_*meo 18 c++ libstdc++ language-lawyer move-semantics c++14
鉴于此计划:
struct Val
{
Val() = default;
Val(Val&&) = default;
auto& operator=(Val&&);
};
/* PLACEHOLDER */
auto& Val::operator=(Val&&) { return *this; }
Run Code Online (Sandbox Code Playgroud)
/* PLACEHOLDER */用...... 代替
int main()
{
std::vector<std::pair<int, Val>> v;
v.emplace(std::begin(v), 0, Val{});
}
Run Code Online (Sandbox Code Playgroud)
...成功编译:
g ++ 7.0.1(主干)
clang ++ 3.9.1
/* PLACEHOLDER */用...... 代替
template <typename TVec>
void a(TVec& v)
{
v.emplace(std::begin(v), 0, Val{});
}
int main()
{
std::vector<std::pair<int, Val>> v;
a(v);
}
Run Code Online (Sandbox Code Playgroud)
...成功编译:
...但是会产生编译时错误:
产生的错误似乎与受约束的pair operator=(pair&&)重载有关 - 来自include/bits/stl_pair.hGitHub的libstdc ++镜像:
pair&
operator=(typename conditional<
__and_<is_move_assignable<_T1>,
is_move_assignable<_T2>>::value,
pair&&, __nonesuch&&>::type __p)
noexcept(__and_<is_nothrow_move_assignable<_T1>,
is_nothrow_move_assignable<_T2>>::value)
{
first = std::forward<first_type>(__p.first);
second = std::forward<second_type>(__p.second);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
代is_move_assignable<_T2>与std::true_type允许代码进行编译.
移动Val::operator=(Val&&)之前的定义/* PLACEHOLDER */允许代码编译.
更改auto& Val::operator=(Val&&)为Val& Val::operator=(Val&&)允许代码编译.
这里发生了什么?这是最新版本的libstdc ++中的实现缺陷吗?或者旧版本是否错误地编译了错误的代码?
编辑:正如AndyG在他的(现已删除的)答案中发现的那样,在调用之前调用空函数时也会发生错误emplace:
template <typename TVec>
void a(TVec&) { }
int main()
{
std::vector<std::pair<int, Val>> v;
a(v);
v.emplace(std::begin(v), 0, Val{});
}
Run Code Online (Sandbox Code Playgroud)
Commeting出a(v);上述防止编译时错误被产生.g ++ 7和clang ++ 5中都存在此行为.
另一个奇怪的案例是由Sergey Murzin发现的,可以在wandbox上测试:
int main()
{
std::vector<std::pair<int, Val>> v;
v.emplace(v.begin(), 0, Val{});
std::cout << v.back().first << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码产生编译器错误.注释掉包含的行std::cout可以防止错误发生.g ++ 7和clang ++ 5中都存在此行为.
这几乎可以肯定是一个实例化问题.如果你做了一些触发实例化pair<int, Val>定义的东西main,那么就会得到错误.否则,它仅在实例化时vector::emplace被实例化,这里所讨论的实现遵循转换单元的末尾(允许,参见[temp.point]/8),此时赋值运算符是完全定义和可调用的.
a(v)触发定义的实例化,pair<int, Val>因为它是ADL所需要的.如果你写::a(v)或(a)(v)(都抑制ADL),那么错误消失.(显然,a.back().first需要实例化 pair<int, Val>:您正在访问其数据成员.)
| 归档时间: |
|
| 查看次数: |
384 次 |
| 最近记录: |