Created attachment 43644 [details] Test case For a template class with explicit copy and move constructors, passing a std::vector of std::string using initializer list syntax fails because of geing ambiguous. Attached test example. main.cc: In function ‘int main(int, char**)’: main.cc:22:62: error: call of overloaded ‘Foo(<brace-enclosed initializer list>)’ is ambiguous static Foo<std::vector<std::string>> vector({"a", "b", "c"}); ^ main.cc:14:3: note: candidate: Foo<T>::Foo(const Foo<T>&) [with T = std::vector<std::__cxx11::basic_string<char> >] <deleted> Foo(const Foo&) = delete; ^~~ main.cc:12:12: note: candidate: Foo<T>::Foo(T&&) [with T = std::vector<std::__cxx11::basic_string<char> >] explicit Foo(T&& x) { } ^~~ main.cc:11:12: note: candidate: Foo<T>::Foo(const T&) [with T = std::vector<std::__cxx11::basic_string<char> >] explicit Foo(const T& x) { } This test case works properly on clang (it resolves to the move constructor.
(In reply to Jose Dapena Paz from comment #0) > For a template class with explicit copy and move constructors, Those aren't copy and move constructors. Reduced: struct X { X(const char*) { } }; template <typename T> class Foo { public: template <typename... Args> explicit Foo(Args&&... args); explicit Foo(const T&); explicit Foo(T&&) { } Foo(const Foo&) = delete; Foo& operator=(const Foo&) = delete; ~Foo() = default; }; int main() { Foo<X> v({"a"}); } vs.cc: In function 'int main()': vs.cc:22:17: error: call of overloaded 'Foo(<brace-enclosed initializer list>)' is ambiguous Foo<X> v({"a"}); ^ vs.cc:15:3: note: candidate: 'Foo<T>::Foo(const Foo<T>&) [with T = X]' <deleted> Foo(const Foo&) = delete; ^~~ vs.cc:13:12: note: candidate: 'Foo<T>::Foo(T&&) [with T = X]' explicit Foo(T&&) { } ^~~ vs.cc:12:12: note: candidate: 'Foo<T>::Foo(const T&) [with T = X]' explicit Foo(const T&); ^~~
Further reduced: struct X { X(int) { } }; struct Foo { explicit Foo(const X&); explicit Foo(X&&) { } }; int main() { Foo v({1}); } vs.cc: In function 'int main()': vs.cc:11:12: error: call of overloaded 'Foo(<brace-enclosed initializer list>)' is ambiguous Foo v({1}); ^ vs.cc:7:12: note: candidate: 'Foo::Foo(X&&)' explicit Foo(X&&) { } ^~~ vs.cc:6:12: note: candidate: 'Foo::Foo(const X&)' explicit Foo(const X&); ^~~ vs.cc:5:8: note: candidate: 'constexpr Foo::Foo(const Foo&)' struct Foo { ^~~ vs.cc:5:8: note: candidate: 'constexpr Foo::Foo(Foo&&)'
+mpolacek in case he'd like to take a look. This is still reproducible with trunk.
Thanks, this sounds interesting.
This is also ambiguous and seems to have the same cause (i.e. overload resolution accepts list-initialization that calls explicit constructor when forming the implicit conversion sequence): template<class> struct in_place_type_t { explicit in_place_type_t() = default; }; struct A { }; int f(A); int f(in_place_type_t<A>); int x = f({});
Here is a possibly related case: #include <tuple> template <class T> struct pair { using value_type = pair<std::remove_reference_t<T>>; T a, b; constexpr pair& operator=(value_type const& other) { a = other.a; b = other.b; return *this; } constexpr pair& operator=(value_type&& other) { a = std::move(other.a); b = std::move(other.b); return *this; } }; template <class T> constexpr pair<T&> tie(T& a, T& b) noexcept { return { a, b }; } int main() { int a = 3; int b = 5; tie(a, b) = { b, a % b }; // works tie(a, b) = { b, a }; // wat } Error messages are very similar https://godbolt.org/z/4FSeOO.
(In reply to Zhihao Yuan from comment #6) > Here is a possibly related case: > > [...] I think this is a different bug. GCC thinks the implicitly-deleted move assignment operator `pair<int&>& pair<int&>::operator=(pair<int&>&&)` is a candidate for the assignment, which causes ambiguity with `operator=(value_type&&)` (where value_type = pair<int>). But as part of resolution of CWG 1402, [class.copy.assign] specifies that "A defaulted move assignment operator that is defined as deleted is ignored by overload resolution".
PR 97220 looks similar and might be a dup of this bug.
I'm not marking this as a dup of PR60027 because of the specific case of copy/move constructors, which cannot be constrained to avoid this problem. The patch in PR109247 fixes the first three testcases, which fall into that category, but not the testcase in #5, which does not. The testcase in #6 is unrelated; there is no "explicit". Unassigning from Marek.
(In reply to ensadc from comment #7) > (In reply to Zhihao Yuan from comment #6) > > I think this is a different bug. GCC thinks the implicitly-deleted move > assignment operator `pair<int&>& pair<int&>::operator=(pair<int&>&&)` is a > candidate for the assignment, which causes ambiguity with > `operator=(value_type&&)` (where value_type = pair<int>). But as part of > resolution of CWG 1402, [class.copy.assign] specifies that "A defaulted move > assignment operator that is defined as deleted is ignored by overload > resolution". Incidentally, no: GCC mentions it, but considers it worse than any other candidate. The real ambiguity is with operator=(const pair<int&>&), the implicitly-deleted *copy* assignment. I don't think this is a bug; if you disagree, please open another PR for it.
DR1228 was closed as NAD.