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.

下面是一些其他扩展。

  1. 你可能会在有些源码中看到 explicit(true) 或是 explicit(false) 这些的代码,乍看之下好像多此一举,不明所以。这种方式的目的其实跟写注释差不多,就是标明函数是 explicit 还是 implicit,一目了然;
  2. explicit(bool) 也可以结合 Concepts 使用,std::pair 的实现就是一个例子;
  3. explicit(bool) 能够避免疏忽导致的昂贵转换开销,并且能够保持类型语义安全;
  4. 还有一种用法是根据参数包来动态决定是否 explicit,但是具体什么时候用得上,可能得真有需求了才能知道。
struct Foo {
    template <typename ... Ts>
    explicit(sizeof...(Ts) == 1) Foo(Ts&&...) {}
};

Foo good = {1,2}; // OK
Foo bad = {1};    // error

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use the Markdown in the comment form.