T230418 explicit(bool)
explicit(bool)
是 C++20 引入的一个特性,称为 Conditionally explicit。
核心目的是简化泛型类型的实现,提高性能,减少编译时间。
举个简单的例子:
void foo(std::string);
foo("a"); // OK
foo("a"sv); // Error
因为没有相应的稳式转换, std::string
对于 std::string_view
的构造是 explicit
的,所以编译失败。这是普通的 explicit
用法,若 foo()
的参数变为 wrapper<std::string>
,这里的 wrapper
就是前面所说的泛型类型,此时依旧想保持 foo()
的调用行为,在 C++20 之前可以使用 SFINAE。
template<class T>
struct wrapper {
template<class U, std::enable_if_t<std::is_convertible_v<U, T>>* = nullptr>
wrapper(U const& u) : t_(u) {}
template<class U, std::enable_if_t<!std::is_convertible_v<U, T>>* = nullptr>
explicit wrapper(U const& u) : t_(u) {}
T t_;
};
这里通过 SFINAE 动态地设置了 explicit
,除去 SFINAE 本身的缺点,一种显而易见的缺点就是得写两个重载函数。此时就可以使用 explicit(bool)
来简化代码:
template<class T>
struct wrapper {
template<class U>
explicit(!std::is_convertible_v<U, T>)
wrapper(U const& u) : t_(u) {}
T t_;
};
这就是 Conditionally explicit 的原理与目的,标准中的许多类型,都使用了该手法,比如 std::pair
, std::span
, std::optional
, etc.
下面是一些其他扩展。
- 你可能会在有些源码中看到
explicit(true)
或是explicit(false)
这些的代码,乍看之下好像多此一举,不明所以。这种方式的目的其实跟写注释差不多,就是标明函数是explicit
还是implicit
,一目了然; explicit(bool)
也可以结合 Concepts 使用,std::pair
的实现就是一个例子;explicit(bool)
能够避免疏忽导致的昂贵转换开销,并且能够保持类型语义安全;- 还有一种用法是根据参数包来动态决定是否
explicit
,但是具体什么时候用得上,可能得真有需求了才能知道。
struct Foo {
template <typename ... Ts>
explicit(sizeof...(Ts) == 1) Foo(Ts&&...) {}
};
Foo good = {1,2}; // OK
Foo bad = {1}; // error