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

Я задал вопрос об этом и не получил четкого ответа, но после прочтения эта статья Я начал предпочитать 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* здесь?


person Jonathan Mee    schedule 11.12.2018    source источник
comment
@SergeyA Тьфу, кажется, я только один раз написал с ошибкой, верно? (В названии Т.Т.)   -  person Jonathan Mee    schedule 11.12.2018
comment
Является ли bar константой времени компиляции?   -  person NathanOliver    schedule 11.12.2018
comment
@NathanOliver Я имею в виду, что в моем случае это не так, но если бы мы могли получить частичный ответ для случаев, когда bar была константой времени компиляции, я бы согласился.   -  person Jonathan Mee    schedule 11.12.2018
comment
Я, хотя я сделал, но это не сработало. Язык действительно ограничительный здесь.   -  person NathanOliver    schedule 11.12.2018


Ответы (3)


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

// 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");
person Artyer    schedule 11.12.2018

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

Однако это будет для std::arrays, если вы явно укажете тип (и предполагая С++ 17):

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

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

person SergeyA    schedule 11.12.2018
comment
@NathanOliver, конечно, того же типа. Это требуется для тернарного оператора — левая и правая стороны должны иметь одинаковый размер. Это действительно хороший момент. - person SergeyA; 11.12.2018
comment
К сожалению, в моем случае речь идет о двух буквальных c-строках разной длины. Так что это может быть полезно в другом случае, но в моем случае мне просто нужно использовать const char*. Если никто не даст лучшего ответа, я приму это завтра. - person Jonathan Mee; 11.12.2018

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

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

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

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

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

    const char z[] = {'a','b'};
    const char c[] = "abcd";
    //no other way to deduce the size
    
person Oliv    schedule 11.12.2018