显式(布尔)的用例是什么


24

C ++ 20引入了显式(布尔),它在编译时有条件地选择是否使构造函数显式。

下面是我在这里找到的示例。

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

有人可以告诉我explicit (bool)除使用以外的其他用例std::is_integral吗?


1
一个例子是,实现像tuple该功能那样的条件显式构造函数变得容易得多。
执政官

1
这不是一个正确的答案,但您也可以查看介绍它的论文的动机:wg21.link/p0892
N.

例如:它(与概念一起)将切断所需数量的基类的实现有条件地提供有条件明确的拷贝构造的3至0。
LF

Answers:


21

动机本身可以在本文中看到。

有必要使构造函数有条件地明确。也就是说,您想要:

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-specifier:

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&& );   
};

这样可以更好地匹配意图,减少编写代码,并且在重载解析过程中减少编译器要做的工作(因为必须选择的构造函数较少)。


1
C ++ 20还提供了将enable_if_t零件更改为更漂亮,更简单的约束的能力,可能使用概念。但这与这个问题无关。
aschepler

2

我看到的另一种可能用法是可变参数模板:

通常,默认情况下,explicit构造函数只带有一个参数是很好的(除非需要进行转换)。

所以

struct Foo
{
    template <typename ... Ts>
    explicit(sizeof...(Ts) == 1) Foo(Ts&&...);

    // ...
};

0

我可以看到一个用例,explicit当输入可能是类似于视图的类型(原始指针std::string_view)时,有条件地要求该条件,新对象将在调用后保留(仅复制视图,而不是其引用的内容),并依赖于查看对象的生存期),也可以是类似值的类型(获取副本的所有权,而没有外部生存期的依赖)。

在这种情况下,调用方负责使查看的对象保持活动状态(被调用方拥有一个视图,而不是原始对象),并且不应隐式完成转换,因为这样会使隐式创建的对象很容易超过了它查看的对象。相比之下,对于值类型,新对象将收到其自己的副本,因此,虽然副本可能成本很高,但是如果发生隐式转换,则不会使代码出错

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.