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

问题描述 投票:7回答:2

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之外,有人可以告诉我显式(布尔)的其他用例吗?

c++ c++20 explicit
2个回答
3
投票

动机本身可以在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&& );   
};

这都可以更好地匹配意图,并且编写的代码要少得多。


0
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.