Я спросил вопрос по этому поводу и не получил действительно четкий ответ, но после прочтения Эта статья Я начал предпочитать const char[]
в const char*
,
Я столкнулся с трудностью при инициализации с троичным. Дано const bool bar
, Я пробовал:
const char foo[] = bar ? "lorem" : "ipsum"
что дает мне ошибку:ошибка: инициализатор не может определить размер
foo
const char foo[] = bar ? { 'l', 'o', 'r', 'e', 'm', '\0' } : { 'i', 'p', 's', 'u', 'm', '\0' }
что дает мне ошибку:ошибка: ожидаемое первичное выражение до
{
знак
Есть ли способ инициализировать const char []
с тройной, или я должен переключиться на const char*
Вот?
Поскольку строковые литералы являются 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");
Невозможно инициализировать массив символов с помощью троичного оператора. Причина этого заключается в том, что обе стороны тернарного оператора фактически используются для создания объекта, а затем объект используется для инициализации значения. Поскольку вы не можете инициализировать один массив из другого, троичная инициализация для массивов не работает.
Это было бы для std::arrays
тем не менее, при условии, что вы явно указали тип (и предполагая, что C ++ 17):
std::array k = b ? std::array{1, 2, 3, 4} : std::array{ 5, 6, 7 ,8};
Обратите внимание, что массивы должны быть одинакового размера. В этом контексте вообще нельзя использовать массивы разных размеров, поскольку обе стороны тернарного оператора должны быть одного типа (а размер массива является частью его типа). В случае, если ваши строки имеют разные размеры, вам придется использовать const char* const
,
Пока два строковых литерала имеют одинаковый размер, результат троичного оператора ссылается на любой из строковых литералов, и этот результат имеет тип массива:
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