在具有显式默认构造函数的类型的复制初始化上下文中,来自 {} 的隐式转换序列
Implicit conversion sequence from {} in a copy-initialization context for a type with an explicit default constructor
(考虑 C++17 及以后的问题)
LWG issue 3562(+),是否可以用 explicit
显式默认取代 nullopt_t
的非 DefaultConstructible 要求其他标签类型的默认构造函数:
struct nullopt_t { explicit nullopt_t() = default; };
因非缺陷 (NAD) 而关闭,理由如下
2021-06-14 Reflector poll:
Current wording prevents an implicit conversion sequence from {}
.
(+) 请注意,此问题同时提到了 LWG 问题 2510 和 CWG 问题 1518 和 1630,它们的决议 (changed/superseded) 紧密耦合。
考虑以下示例:
struct my_nullopt_t {
explicit constexpr my_nullopt_t() = default;
};
struct S {
constexpr S() {} // user-provided: S is not an aggregate
constexpr S(S const&) = default;
S& operator=(S const&) = default; // #1
S& operator=(my_nullopt_t) = delete; // #2
};
int main() {
S s{};
s = {}; // #3
}
我希望它的格式正确,因为重载 #2
对于 #3
处的赋值不可行,因为 my_nullopt_t
的默认构造函数是 explicit
(意思是 my_nullopt_t
而且不是 C++17 中的聚合)。我特别认为这是由 [over.match.ctor]/1:
控制的
When objects of class type are direct-initialized, copy-initialized from an expression of the same or a derived class type ([dcl.init]), or default-initialized, overload resolution selects the constructor. For direct-initialization or default-initialization that is not in the context of copy-initialization, the candidate functions are all the constructors of the class of the object being initialized. For copy-initialization (including default initialization in the context of copy-initialization), the candidate functions are all the converting constructors ([class.conv.ctor]) of that class. [...]
[class.conv.ctor]/1 A constructor that is not explicit ([dcl.fct.spec]) specifies a conversion from the types of its parameters (if any) to the type of its class. Such a constructor is called a converting constructor.
但考虑到反射器上面的评论,我可能错了。可能是[over.match.list]/1
In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This differs from other situations ([over.match.ctor], [over.match.copy]), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution. — end note ]
通过来自 [over.ics.list]/7?
的条目来管理此案例
我们最终可能会注意到,我们在这里也看到了编译器差异,其中 Clang 接受程序而 GCC 拒绝它(对于 C++11 到 C++20)并出现歧义错误。 GCC 似乎特别认为 #2
是一个可行的重载,而 Clang 则不然。
GCC error: ambiguous overload for 'operator='
问题
- 什么标准段落支持反射器上面的基本原理,例如应用于上面的例子?
- 这是否支持GCC拒绝上面的程序,以至于Clang接受它是错误的?
GCC是对的,Clang和MSVC是错的。这突出显示在:
也作为 NAD 关闭。
有些令人惊讶的是,关于 explicit
构造函数的规则在复制列表初始化 ([over.match.list]) 和复制初始化 ([over.match.copy]) 之间有所不同 [强调我的]:
1228. Copy-list-initialization and explicit constructors
- Section: 12.2.2.8 [over.match.list]
- Status: NAD
- Submitter: Daniel Krügler
- Date: 2010-12-03
The rules for selecting candidate functions in
copy-list-initialization (12.2.2.8 [over.match.list]) differ from
those of regular copy-initialization (12.2.2.5 [over.match.copy]): the
latter specify that only the converting (non-explicit) constructors
are considered, while the former include all constructors but state
that the program is ill-formed if an explicit constructor is selected
by overload resolution. This is counterintuitive and can lead to
surprising results. For example, the call to the function object p
in the following example is ambiguous because the explicit constructor
is a candidate for the initialization of the operator's parameter:
struct MyList {
explicit MyStore(int initialCapacity);
};
struct MyInt {
MyInt(int i);
};
struct Printer {
void operator()(MyStore const& s);
void operator()(MyInt const& i);
};
void f() {
Printer p;
p({23});
}
Rationale (March, 2011):
The current rules are as intended.
(考虑 C++17 及以后的问题)
LWG issue 3562(+),是否可以用 explicit
显式默认取代 nullopt_t
的非 DefaultConstructible 要求其他标签类型的默认构造函数:
struct nullopt_t { explicit nullopt_t() = default; };
因非缺陷 (NAD) 而关闭,理由如下
2021-06-14 Reflector poll:
Current wording prevents an implicit conversion sequence from
{}
.
(+) 请注意,此问题同时提到了 LWG 问题 2510 和 CWG 问题 1518 和 1630,它们的决议 (changed/superseded) 紧密耦合。
考虑以下示例:
struct my_nullopt_t {
explicit constexpr my_nullopt_t() = default;
};
struct S {
constexpr S() {} // user-provided: S is not an aggregate
constexpr S(S const&) = default;
S& operator=(S const&) = default; // #1
S& operator=(my_nullopt_t) = delete; // #2
};
int main() {
S s{};
s = {}; // #3
}
我希望它的格式正确,因为重载 #2
对于 #3
处的赋值不可行,因为 my_nullopt_t
的默认构造函数是 explicit
(意思是 my_nullopt_t
而且不是 C++17 中的聚合)。我特别认为这是由 [over.match.ctor]/1:
When objects of class type are direct-initialized, copy-initialized from an expression of the same or a derived class type ([dcl.init]), or default-initialized, overload resolution selects the constructor. For direct-initialization or default-initialization that is not in the context of copy-initialization, the candidate functions are all the constructors of the class of the object being initialized. For copy-initialization (including default initialization in the context of copy-initialization), the candidate functions are all the converting constructors ([class.conv.ctor]) of that class. [...]
[class.conv.ctor]/1 A constructor that is not explicit ([dcl.fct.spec]) specifies a conversion from the types of its parameters (if any) to the type of its class. Such a constructor is called a converting constructor.
但考虑到反射器上面的评论,我可能错了。可能是[over.match.list]/1
In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This differs from other situations ([over.match.ctor], [over.match.copy]), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution. — end note ]
通过来自 [over.ics.list]/7?
的条目来管理此案例我们最终可能会注意到,我们在这里也看到了编译器差异,其中 Clang 接受程序而 GCC 拒绝它(对于 C++11 到 C++20)并出现歧义错误。 GCC 似乎特别认为 #2
是一个可行的重载,而 Clang 则不然。
GCC error: ambiguous overload for 'operator='
问题
- 什么标准段落支持反射器上面的基本原理,例如应用于上面的例子?
- 这是否支持GCC拒绝上面的程序,以至于Clang接受它是错误的?
GCC是对的,Clang和MSVC是错的。这突出显示在:
也作为 NAD 关闭。
有些令人惊讶的是,关于 explicit
构造函数的规则在复制列表初始化 ([over.match.list]) 和复制初始化 ([over.match.copy]) 之间有所不同 [强调我的]:
1228. Copy-list-initialization and explicit constructors
- Section: 12.2.2.8 [over.match.list]
- Status: NAD
- Submitter: Daniel Krügler
- Date: 2010-12-03
The rules for selecting candidate functions in copy-list-initialization (12.2.2.8 [over.match.list]) differ from those of regular copy-initialization (12.2.2.5 [over.match.copy]): the latter specify that only the converting (non-explicit) constructors are considered, while the former include all constructors but state that the program is ill-formed if an explicit constructor is selected by overload resolution. This is counterintuitive and can lead to surprising results. For example, the call to the function object p in the following example is ambiguous because the explicit constructor is a candidate for the initialization of the operator's parameter:
struct MyList { explicit MyStore(int initialCapacity); }; struct MyInt { MyInt(int i); }; struct Printer { void operator()(MyStore const& s); void operator()(MyInt const& i); }; void f() { Printer p; p({23}); }
Rationale (March, 2011):
The current rules are as intended.