какой случай использования для явного (bool) - PullRequest
24 голосов
/ 12 марта 2020

C ++ 20 введено явный (bool) , который условно выбирает во время компиляции, сделан ли конструктор явным или нет.

Ниже приведен пример который я нашел здесь .

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?

Ответы [ 3 ]

21 голосов
/ 12 марта 2020

Саму мотивацию можно увидеть в статье .

Необходимо сделать конструкторы условно явными. То есть вы хотите:

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

Это лучше соответствует намерениям, намного меньше кода для написания и меньше работы для компилятора во время разрешения перегрузки (так как между ними нужно выбирать меньше конструкторов).

2 голосов
/ 12 марта 2020

Другое возможное использование, которое я вижу, - это variadi c template:

Обычно по умолчанию хорошо иметь explicit для конструктора только с одним аргументом (если не требуется преобразование).

так

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

    // ...
};
0 голосов
/ 12 марта 2020

Я мог бы видеть вариант использования для условного требования explicit, когда входные данные могут быть типа, подобного представлению (необработанный указатель, std::string_view), к которому новый объект будет удерживаться после вызова (только копирование представления, не то, к чему он относится, оставаясь зависимым от времени жизни просматриваемого объекта), или это может быть тип, подобный значению (становится владельцем копии, без внешних зависимостей времени жизни).

В ситуации, подобной что вызывающий отвечает за поддержание живого просматриваемого объекта (вызываемому принадлежит представление, а не исходный объект), и преобразование не должно выполняться неявным образом, поскольку для неявно созданного объекта слишком легко пережить объект, который он взгляды. Напротив, для типов значений новый объект получит свою собственную копию, поэтому, хотя копия может быть дорогостоящей, она не сделает код неправильным , если произойдет неявное преобразование.

...