Могу ли я инициализировать символ [] с помощью троичного? - PullRequest
0 голосов
/ 11 декабря 2018

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

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

  1. 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* здесь?

Ответы [ 3 ]

0 голосов
/ 11 декабря 2018

Поскольку строковые литералы являются 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");
0 голосов
/ 11 декабря 2018

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

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
    
0 голосов
/ 11 декабря 2018

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

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

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

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

...