Я написал следующий код в качестве упражнения о шаблонах функций и специализациях шаблонов. Это функция, которая подсчитывает, сколько объектов данного типа присутствует в vector
:
template <typename T>
int function(const std::vector<T> &vec, T val) {
int count = 0;
for(typename std::vector<T>::const_iterator it = vec.begin(); it != vec.end(); ++it)
if(*it == val)
++count;
return count;
}
template <>
int function(const std::vector<const char*> &vec, const char* val) {
int count = 0;
for(std::vector<const char*>::const_iterator it = vec.begin(); it != vec.end(); ++it) {
if (std::string(*it) == std::string(val))
++count;
}
return count;
}
Я написал код в специализации, потому что хочу знать, совпадает ли каждый символ в слове с данным литералом. К моему удивлению, если я закомментирую специализацию и позволю компилятору создать исходный шаблон, он будет работать даже для массивов const char:
int main() {
std::vector<const char*> cvec = {"hi", "hi", "hallo", "hej", "hej", "hi", "hej", "hej", "hej"};
std::cout << function(cvec, "hej") << std::endl;
}
Как это могло быть?
Я использую код в своей основной функции, чтобы протестировать его, но, к моему удивлению, если я закомментирую специализацию и позволю компилятору создать исходный шаблон, он будет работать даже для массивов const char (тип строкового литерала) ! Как это могло быть?
Стандарт не устанавливает, что строковые литералы, содержащие одинаковую последовательность символов, должны храниться в той же ячейке памяти, что и в §2.13.5 / 16:
Оценка строкового литерала приводит к получению строкового литерала со статической продолжительностью хранения, инициализированного из заданных символов, как указано выше. Все ли строковые литералы различны (то есть хранятся в неперекрывающихся объектах) и не определены ли последовательные вычисления строкового литерала того же самого или другого объекта.
(акцент мой)
но эта реализация разрешена, и это то, что здесь происходит: каждая литеральная строка "hej"
хранится в том же адресе памяти, поэтому ==
на const char*
проверяет, является ли адрес тем же и дает значение true.
Чтобы «доказать» это, нам просто нужно взглянуть на этот пример:
int main() {
std::vector<const char*> cvec = {"hi", "hi", "hallo", "hej", "hej", "hi", "hej", "hej", "hej"};
std::cout << function(cvec, "hej") << '\n';
}
Это дает 5
из-за того, что есть 5 литералов "hej"
в векторе. Но если мы добавим еще char
массив с таким же точным char
s в буквальном смысле, что мы знаем, должен иметь другой адрес:
int main() {
std::string hej = "hej";
std::vector<const char*> cvec = {"hi", "hi", "hallo", "hej", "hej", "hi", "hej", "hej", "hej", hej.c_str()};
std::cout << function(cvec, "hej") << std::endl;
}
Затем мы видим, что количество не меняется.