Возвращение строковых констант в блоке switch-case с помощью std :: string - PullRequest
0 голосов
/ 04 февраля 2019

ПРИМЕЧАНИЕ. Это , а не об использовании строки для выбора пути выполнения в блоке переключателя.

Обычный шаблон в C ++ - это использование блока переключателя дляпреобразование целочисленных констант в строки.Это выглядит следующим образом:

char const * to_string(codes code)
{
    switch (code)
    {
        case codes::foo: return "foo";
        case codes::bar: return "bar";
    }
}

Однако мы находимся в C ++, поэтому использование std :: string более уместно:

std::string to_string(codes code)
{
    switch (code)
    {
        case codes::foo: return "foo";
        case codes::bar: return "bar";
    }
}

Это, однако, копирует строковый литерал.Возможно, лучшим подходом было бы вместо этого:

std::string const & to_string(codes code)
{
    switch (code)
    {
        case codes::foo: { static std::string str = "foo"; return str; }
        case codes::bar: { static std::string str = "bar"; return str; }
    }
}

Но это несколько уродливо и требует большего количества шаблонов.

Что считается самым чистым и наиболее эффективным решением этой проблемы с использованием C ++14

Ответы [ 3 ]

0 голосов
/ 04 февраля 2019

Но это довольно уродливо и требует большего количества шаблонов.

Что считается самым чистым и наиболее эффективным решением этой проблемы с использованием C ++ 14?

Чтобы ответить на вышесказанное, как указал @SamerTufail (и так же, как я делаю это сам на работе), я бы использовал enum s и std::map следующим образом.

   typedef enum {
        foo = 1,
        bar = 2,
    } Key;

std::map<Key, std::string> hash_map = { {Key::foo ,"foo"}, { Key::bar,"bar"} };

И затем в main () вы можете получить значение, подобное этому,

std::cout << hash_map.find(Key::foo)->second;

Я бы создал функцию для возврата second, где вы бы проверяли итератор на end(), в противном случае интегратор был бы недействительным и использовал бы егобудет UB.


РЕДАКТИРОВАТЬ: Как и другие отмечали в комментариях и согласно этому вопросу , вы можете заменить std::map на std::unordered_map при условии, что вам не нужно держать элементы в порядке.

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

0 голосов
/ 08 февраля 2019

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

1: в to_string()
2: в вызывающей стороне

Используя Compiler Explorer довольно легко узнать.

Оказывается (с недавними компиляторами), что между ними нет большой разницы.Возвращение const char * имеет небольшое преимущество на std::string

1:

#include <string> 

char const * to_string(int code)
{
    switch (code)
    {
        case 0: return "foo";
        case 1: return "bar";
    }
}

std::string foo(int x)
{
    std::string s{to_string(x)};
    return s;    
}

2:

#include <string> 

std::string to_string2(int code)
{
    switch (code)
    {
        case 0: return "foo";
        case 1: return "bar";
    }
}


std::string foo2(int x)
{
    std::string s{to_string2(x)};
    return s;    
}

Примечание:

  • Мне нужно было добавить foo(), чтобы остановить оптимизацию компилятора еще сильнее ...
  • В обоих случаях строки короткие и могут использовать оптимизацию коротких строк.И Clang, и GCC управляют heap-elision .Это действительно впечатляет - компилятор знает, что to_string() никогда не возвращает строку длиной более 4 байтов, а затем удаляет код, который динамически выделяет кучу памяти.

Вывод, по-видимому, заключается в том, что написание естественного и аккуратного кода приводит к небольшому снижению производительности.

0 голосов
/ 04 февраля 2019

Это, однако, копирует строковый литерал.

Да и нет.Он действительно скопирует строковый литерал, но не обязательно выделяет память.Проверьте свой предел SSO реализации.


Вы можете использовать std::string_view:

constexpr std::string_view to_string(codes code) {
    switch (code) {
        case codes::foo: return "foo";
        case codes::bar: return "bar";
    }
}

Вы можете найти много версий с бэкпортом, таких как эта

Однако иногда char const* - это правильная абстракция.Например, если вам нужно переслать эту строку в API, для которого требуется строка с нулевым символом в конце, вам лучше будет вернуть ее в стиле ac.

...