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