This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2024-10-26
There is implementation divergence in handling the following example:
template <typename _Tp> struct optional { template <typename _Up> explicit optional(_Up); template <typename _Up = _Tp> void operator=(_Up); }; struct SourceBrush { struct Brush { int brush; }; void setPattern() { m_brush = {42}; } optional<Brush> m_brush; };
The following example is ambiguous per issue 1228:
#include <unordered_set> #include <string> template<typename T> struct wrap { template<typename ...Ts> explicit wrap(Ts &&...args) : v(std::forward<Ts>(args)...) {} explicit wrap(const T &v) : v(v) {} explicit wrap(T &&v) : v(v) {} wrap(const wrap&) = delete; T v; }; void f() { wrap<std::unordered_set<std::string>> wrapped_set({"foo", "bar", "baz"}); }
The copy constructor of wrap<...> becomes viable, by way of constructing another wrap object from the given initializer list using the explicit constructor template. It looks like a deleted copy constructor is used to remove a level of braces, and then picking an explicit constructor to construct the source of the copy.
Another example:
#include <string> #include <map> struct X { explicit X(const std::map<std::string, std::string> &map); }; struct Y { Y() : x({{"foo", "bar"}}) {} X x; };
The intent is to construct the map with a single key-value pair, but the list-initialization is ambiguous with invoking the copy constructor and creating a map from a pair of iterators given by {"foo", "bar"}.
And another example:
struct Z {}; struct X { explicit X(const Z &z = {}); }; struct Y { Y() : x({}) {} X x; };
The ambiguity is between
Core issue 2267 is also related.
Suggested resolution:
Insert before 12.2.4.1 [over.match.best.general] bullet 2.4 as follows:
- ...
- F2 is a copy or move special member function where the implicit conversion sequence for its (non-object) argument is a user-defined conversion sequence specified by a non-converting constructor (considered under 12.2.2.8 [over.match.list]) and F1 is not, or, if not that, [ Example:
struct Z {}; struct X { explicit X(); // #1 explicit X(const Z &z); // #2 }; struct Y { Y() : x({}) {} // OK, calls #2, not ambiguous with the copy constructor of X using a temporary initialized by #1 X x; };-- end example ]- F1 is not a function template specialization and F2 is a function template specialization, or, if not that,
- ...