корректная идиома для станд.:: строковые константы?

Слот должен существовать для вас, чтобы иметь возможность перечислить ему другую переменную (что на самом деле происходит здесь, «ссылки» PHP не являются действительно реальными вещами, по ссылке "операция является регулярной операцией с копированием, замененной на псевдонимом), но она не должна содержать ненулевое значение и не до тех пор, пока вы ее не присвоите ей (через любое из ее имен).

24
задан pm100 22 February 2010 в 21:58
поделиться

7 ответов

Копирование и отсутствие «строковой литеральной оптимизации» - это то, как работает std :: strings, и вы не можете получить именно то, что просите. Частично это связано с тем, что виртуальных методов и dtor явно избегали. Интерфейс std :: string много сложен без них, во всяком случае.

Стандарт требует определенного интерфейса как для std :: string, так и для std :: map, и эти интерфейсы запрещают оптимизацию, которую вы хотели бы (как «непреднамеренное следствие» других его требований, а не явно). По крайней мере, они запрещают это, если вы действительно хотите следовать всем мелким деталям стандарта. И вы действительно этого хотите, особенно когда так легко использовать другой класс строк для этой конкретной оптимизации.

Тем не менее, этот отдельный строковый класс может решить эти «проблемы» (как вы сказали, это редко является проблемой), но, к сожалению, в мире number_of_programmers + 1 таких уже есть. Даже принимая во внимание это изобретение колеса, я нашел полезным иметь класс StaticString, который имеет подмножество интерфейса std :: string: использование begin / end, substr, find и т. Д. Он также запрещает модификацию (и подходит для строковых литералов). таким образом), сохраняя только указатель на символ и размер. Вы должны быть немного осторожны, чтобы он инициализировался только строковыми литералами или другими «статическими» данными, но это несколько смягчается интерфейсом построения:

struct StaticString {
  template<int N>
  explicit StaticString(char (&data)[N]); // reference to char array
  StaticString(StaticString const&); // copy ctor (which is very cheap)

  static StaticString from_c_str(char const* c_str); // static factory function
  // this only requires that c_str not change and outlive any uses of the
  // resulting object(s), and since it must also be called explicitly, those 
  // requirements aren't hard to enforce; this is provided because it's explicit
  // that strlen is used, and it is not embedded-'\0'-safe as the
  // StaticString(char (&data)[N]) ctor is

  operator char const*() const; // implicit conversion "operator"
  // here the conversion is appropriate, even though I normally dislike these

private:
  StaticString(); // not defined
};

Использование:

StaticString s ("abc");
assert(s != "123"); // overload operators for char*
some_func(s); // implicit conversion
some_func(StaticString("abc")); // temporary object initialized from literal

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

.

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

18
ответ дан 28 November 2019 в 23:49
поделиться

Похоже, вы уже знаете, какими будут строковые литералы во время выполнения, поэтому вы можете настроить внутреннее отображение между перечисляемыми значениями и массивом строк. Тогда вы будете использовать перечисление вместо фактического константа char * literal в вашем коде.

enum ConstStrings
{
    MAP_STRING,
    FOO_STRING,
    NUM_CONST_STRINGS
};

std::string constStrings[NUM_CONST_STRINGS];

bool InitConstStrings()
{
    constStrings[MAP_STRING] = "map";
    constStrings[FOO_STRING] = "foo";
}

// Be careful if you need to use these strings prior to main being called.
bool doInit = InitConstStrings();

const std::string& getString(ConstStrings whichString)
{
    // Feel free to do range checking if you think people will lie to you about the parameter type.
    return constStrings[whichString];
}

Тогда вы скажете map[getString(MAP_STRING)] или подобное.

Кроме того, также рассмотрите возможность сохранения возвращаемого значения с помощью константной ссылки, а не копирования, если вам не нужно изменять его:

const std::string& val = map["foo"];
1
ответ дан Mark B 28 November 2019 в 23:49
поделиться
  1. Можно избежать накладных расходов на создание std::string, когда все, что вам нужно, это постоянная строка. Но вам нужно написать специальный класс для этого, потому что нет ничего подобного ни в STL, ни в Boost. Или лучшей альтернативой является использование класса, подобного StringPiece из Chromium или StringRef из LLVM. См. связанную тему для получения дополнительной информации.

  2. Если вы решите остаться с std::string (что вы, вероятно, будете), тогда другим хорошим вариантом будет использование контейнера Boost MultiIndex, который имеет следующую особенность (цитируя docs ):

    Boost MultiIndex [...] обеспечивает операции поиска, принимающие ключи поиска, отличные от key_type индекса, что является особенно полезным средством, когда создание объектов key_type является дорогостоящим.

Карты с дорогими ключами Андрея Александреску ( C / C ++ Users Journal , февраль 2006) связаны к вашей проблеме, и это очень хорошее чтение.

6
ответ дан Community 28 November 2019 в 23:49
поделиться

Мое решение (преимущество заключается в возможности использовать функции C ++ 11, которых не было, когда на этот вопрос ранее был дан ответ):

#define INTERN(x) ([]() -> std::string const & { \
    static const std::string y = x; \
    return y; \
}())

my_map[INTERN("key")] = 5;

Да, это макрос, и он может использовать лучшее имя.

0
ответ дан Forrest Voight 28 November 2019 в 23:49
поделиться

Проблема в том, что std::map копирует ключ и значения в свои собственные структуры.

У вас может быть std::map<const char *, const char *>, но вам нужно будет предоставить функциональные объекты (или функции) для сравнения данных ключа и значения, как этот трафарет для указателей. По умолчанию map сравнивает указатели, а не данные, на которые указывают указатели.

Компромисс между одноразовой копией (std::string) и доступом к компаратору (const char *).

Другая альтернатива - написать свою собственную функцию map.

0
ответ дан Thomas Matthews 28 November 2019 в 23:49
поделиться

Это просто: используйте

extern const std::string FOO;

в заголовке и

const std::string FOO("foo");

в соответствующем файле .cpp .

9
ответ дан 28 November 2019 в 23:49
поделиться

Правильная идиома - та, которую вы используете. В 99.99% случаев нет необходимости беспокоиться о накладных расходах конструктора std::string.

Мне интересно, может ли компилятор превратить конструктор std::string во внутреннюю функцию? Теоретически это возможно, но мой комментарий выше достаточно объясняет, почему этого не произошло.

1
ответ дан 28 November 2019 в 23:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: