Given this program:
struct Val
{
Val() = default;
Val(Val&&) = default;
auto& operator=(Val&&);
};
/* PLACEHOLDER */
auto& Val::operator=(Val&&) { return *this; }
Substituting /* PLACEHOLDER */
with...
int main()
{
std::vector<std::pair<int, Val>> v;
v.emplace(std::begin(v), 0, Val{});
}
...compiles successfully on:
- g++ 6.2.0
- g++ 6.3.0
g++ 7.0.1 (trunk)
clang++ 3.9.1
- clang++ 5.0.0 (HEAD)
Substituting /* PLACEHOLDER */
with...
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);
}
...compiles successfully on:
- g++ 6.2.0
- clang++ 3.9.1
...but produces a compile-time error on:
- g++ 6.3.0
- g++ 7.0.1 (trunk)
- clang++ 5.0.0 (HEAD)
The produced error seems to be related to a constrained pair operator=(pair&&)
overload - from include/bits/stl_pair.h
on GitHub's libstdc++ mirror:
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;
}
Substituting
is_move_assignable<_T2>
withstd::true_type
allows the code to compile.Moving the definition of
Val::operator=(Val&&)
before/* PLACEHOLDER */
allows the code to compile.Changing
auto& Val::operator=(Val&&)
toVal& Val::operator=(Val&&)
allows the code to compile.
What is going on here? Is this an implementation defect in the latest version of libstdc++? Or were the older versions incorrectly compiling ill-formed code?
EDIT: as AndyG discovered in his (now deleted) answer, the error also occurs when a call to an empty function is made before invoking 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{});
}
Commeting out a(v);
above prevents the compile-time error from being produced. This behavior is present both in g++7 and clang++5.
Another weird case was discovered by Sergey Murzin, and can be tested out on wandbox:
int main()
{
std::vector<std::pair<int, Val>> v;
v.emplace(v.begin(), 0, Val{});
std::cout << v.back().first << std::endl;
}
The code above produces a compiler error. Commenting out the line containing std::cout
prevents the error from occurring. This behavior is present both in g++7 and clang++5.
pair::operator=
instantiation counts as "determine the type of an expression" – Confection