c ++ 20引入的explicit(bool),它在编译时有条件地选择是否使构造函数显式。
下面是我在这里找到的一个例子https://github.com/AnthonyCalandra/modern-cpp-features
struct foo {
// Specify non-integral types (strings, floats, etc.) require explicit construction.
template <typename T>
explicit(!std::is_integral_v<T>) foo(T) {}
};
foo a = 123; // OK
foo b = "123"; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)
foo c {"123"}; // OK
除了使用std :: is_integral之外,有人可以告诉我显式(布尔)的其他用例吗?
动机本身可以在the paper中看到。
需要使构造函数有条件地显式。也就是说,您想要:
pair<string, string> safe() {
return {"meow", "purr"}; // ok
}
pair<vector<int>, vector<int>> unsafe() {
return {11, 22}; // error
}
前者很好,那些构造函数是隐式的。但是后者会很糟糕,那些构造函数是explicit
。使用C ++ 17(或带有概念的C ++ 20),完成这项工作的唯一方法是编写两个构造函数-一个explicit
,另一个不是:
template <typename T1, typename T2>
struct pair {
template <typename U1=T1, typename U2=T2,
std::enable_if_t<
std::is_constructible_v<T1, U1> &&
std::is_constructible_v<T2, U2> &&
std::is_convertible_v<U1, T1> &&
std::is_convertible_v<U2, T2>
, int> = 0>
constexpr pair(U1&&, U2&& );
template <typename U1=T1, typename U2=T2,
std::enable_if_t<
std::is_constructible_v<T1, U1> &&
std::is_constructible_v<T2, U2> &&
!(std::is_convertible_v<U1, T1> &&
std::is_convertible_v<U2, T2>)
, int> = 0>
explicit constexpr pair(U1&&, U2&& );
};
这些几乎完全重复-这些构造函数的定义是相同的。
使用explicit(bool)
,您只需编写一个构造函数-构造的有条件显式部分就定位为explicit
-说明符:
template <typename T1, typename T2>
struct pair {
template <typename U1=T1, typename U2=T2,
std::enable_if_t<
std::is_constructible_v<T1, U1> &&
std::is_constructible_v<T2, U2>
, int> = 0>
explicit(!std::is_convertible_v<U1, T1> ||
!std::is_convertible_v<U2, T2>)
constexpr pair(U1&&, U2&& );
};
这都可以更好地匹配意图,并且编写的代码要少得多。
我可以看到一个用例,当输入可能是类似视图的类型(原始指针,explicit
)时,有条件地要求std::string_view
,新对象将在调用后保留(仅复制视图,而不是复制视图)它指的是什么,仍然取决于查看对象的生存期),或者它可能是类似值的类型(获取副本的所有权,没有外部生存期的依赖)。
在这种情况下,调用者负责使查看的对象保持活动状态(被调用者拥有一个视图,而不是原始对象),并且不应隐式地进行转换,因为这样会使隐式创建起来太容易了对象的寿命超过它查看的对象。相比之下,对于值类型,新对象将收到其自己的副本,因此尽管副本可能成本很高,但是如果发生隐式转换,它将不会使代码wrong。