Могу ли я инициализировать символ [] с помощью троичного?

Я спросил вопрос по этому поводу и не получил действительно четкий ответ, но после прочтения Эта статья Я начал предпочитать const char[] в const char*,

Я столкнулся с трудностью при инициализации с троичным. Дано const bool bar, Я пробовал:

  1. const char foo[] = bar ? "lorem" : "ipsum" что дает мне ошибку:

ошибка: инициализатор не может определить размер foo

  1. const char foo[] = bar ? { 'l', 'o', 'r', 'e', 'm', '\0' } : { 'i', 'p', 's', 'u', 'm', '\0' } что дает мне ошибку:

ошибка: ожидаемое первичное выражение до { знак

Есть ли способ инициализировать const char [] с тройной, или я должен переключиться на const char* Вот?

4

Решение

Поскольку строковые литералы являются lvalues, вы можете использовать их константные ссылки, которые могут использоваться в троичной форме.

// You need to manually specify the size
const char (&foo)[6] = bar ? "lorem" : "ipsum";

// Or (In C++11)
auto foo = bar ? "lorem" : "ipsum";

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

Если вы хотите сделать это для строк разной длины, к сожалению «bool ? const char[x] : const char[y]«будет только тип массива, если они имеют одинаковый размер (В противном случае они оба распадутся на указатели, и выражение будет иметь тип const char*). Чтобы исправить это, вы должны вручную дополнить строку \0 персонажи (А теперь вы не можете сделать sizeof(foo) - 1 чтобы получить размер, вам придется сделать strlen(foo)).

Например, вместо:

auto foo = bar ? "abc" : "defg";  // foo is a const char*

Вы должны сделать:

auto foo = bar ? "abc\0" : "defg"; // foo is const char(&)[5]
// Note that if `bar` is true, foo is `{'a', 'b', 'c', '\0', '\0'}`

Прежде чем сделать это, учтите, что если вы установите свои переменные как const char * constваш компилятор, скорее всего, оптимизирует их так, чтобы они были точно такими же, как если бы они были const char[]и, вероятно, также то же самое, если бы они были только const char * (без константы в конце), если вы не измените значение.

Чтобы случайно не получить указатель и быстро потерпеть неудачу, если вы это сделаете, я бы использовал вспомогательную функцию:

#include <cstddef>

template<std::size_t size, std::size_t other_size = size>
constexpr auto conditional(bool condition, const char(&true_case)[size], const char(&false_case)[other_size]) noexcept -> const char(&)[size] {
static_assert(size == other_size, "Cannot have a c-string conditional with c-strings of different sizes");
return condition ? true_case : false_case;
}

// Usage:
auto foo = conditional(bar, "lorem", "ipsum");

Если bar является постоянной времени компиляции, вы можете изменить тип foo в зависимости от стоимости bar, Например:

#include <cstddef>

template<bool condition, std::size_t true_size, std::size_t false_size>
constexpr auto conditional(const char(&true_case)[true_size], const char(&false_case)[false_size]) -> typename std::enable_if<condition, const char(&)[true_size]>::type {
return true_case;
}

template<bool condition, std::size_t true_size, std::size_t false_size>
constexpr auto conditional(const char(&true_case)[true_size], const char(&false_case)[false_size]) -> typename std::enable_if<!condition, const char(&)[false_size]>::type {
return false_case;
}

// Or with C++17 constexpr if

template<bool condition, std::size_t true_size, std::size_t false_size>
constexpr auto conditional(const char(&true_case)[true_size], const char(&false_case)[false_size]) -> const char(&)[condition ? true_size : false_size] {
if constexpr (condition) {
return true_case;
} else {
return false_case;
}
}

// Usage:
auto foo = conditional<bar>("dolor", "sit");
1

Другие решения

Невозможно инициализировать массив символов с помощью троичного оператора. Причина этого заключается в том, что обе стороны тернарного оператора фактически используются для создания объекта, а затем объект используется для инициализации значения. Поскольку вы не можете инициализировать один массив из другого, троичная инициализация для массивов не работает.

Это было бы для std::arraysтем не менее, при условии, что вы явно указали тип (и предполагая, что C ++ 17):

std::array k = b ? std::array{1, 2, 3, 4} : std::array{ 5, 6, 7 ,8};

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

2

Пока два строковых литерала имеют одинаковый размер, результат троичного оператора ссылается на любой из строковых литералов, и этот результат имеет тип массива:

auto& x = true?"1234":"1234";
static_assert(is_same_v<decltype(x),const char (&)[5]>);

Как только результат троичного оператора установлен, применяется обычное правило языка:

  • c-массив не может быть скопирован

    const char y[5] = x; //error
    
  • размер массива c может быть выведен только из списка инициализаторов или для массива char, если инициализатор является строковым литералом:

    const char z[] = {'a','b'};
    const char c[] = "abcd";
    //no other way to deduce the size
    
1
По вопросам рекламы [email protected]