какой случай использования для явного (bool)

24

В 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?

НКАО
источник
1
Одним из примеров является то, что становится намного проще реализовать условно явные конструкторы, подобные тем, для tupleкоторых используется эта функция.
преторианский
1
Не совсем
N. Shead
Пример: он (наряду с концепциями) сокращает необходимое количество базовых классов для реализации условно предоставленного условно-явного конструктора копирования с 3 до 0.
LF

Ответы:

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

Другое возможное использование, которое я вижу, это с помощью шаблона variadic

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

так

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

    // ...
};
Jarod42
источник
0

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

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

ShadowRanger
источник