Строковый литерал в качестве аргумента шаблона

Эти препроцессорные и шаблонные ограничения C++ убивают меня. Цель состоит в том, чтобы преобразовать строковые литералы в целые числа.

template<const char* str>
inline int LiteralToInt(){
    return __COUNTER__;
}

using std::cout;

int main(){
    cout << LiteralToInt<"Hello">();
    cout << LiteralToInt<"No">();
    cout << LiteralToInt<"Hello">();
    return 0;
}

Вывод будет 010, если шаблоны принимают строковые литералы. Есть ли другой способ получить этот вывод и преобразовать строковые литералы в целые числа во время компиляции?

4 ответа

Решение

К сожалению, я не знаю способа сделать именно то, что вы хотите.

Есть ли какие-то ограничения, которые вы можете наложить на строки? Как количество символов? Если вы можете ограничить его до 1-8 символов, вы можете сделать что-то вроде этого:

template <char Ch1, char Ch2 = '\0', char Ch3 = '\0', char Ch4 = '\0', char Ch5 = '\0', char Ch6 = '\0', char Ch7 = '\0', char Ch8 = '\0'>
struct string_hash {
    static const uint64_t value = 
        (static_cast<uint64_t>(Ch1) << 56) | 
        (static_cast<uint64_t>(Ch2) << 48) | 
        (static_cast<uint64_t>(Ch3) << 40) | 
        (static_cast<uint64_t>(Ch4) << 32) | 
        (static_cast<uint64_t>(Ch5) << 24) | 
        (static_cast<uint64_t>(Ch6) << 16) | 
        (static_cast<uint64_t>(Ch7) << 8)  | 
        (Ch8);
};

который в основном, во время компиляции вещи до 8 персонажи в uint64_t, Использование будет выглядеть так:

const uint64_t x = string_hash<'T', 'e', 's', 't'>::value

Это создаст числовое значение времени компиляции (может использоваться в switch и все такое совершенство) уникален для каждой строки длиной 1-8 символов. К сожалению, единственным большим недостатком является то, что вы не можете написать его в виде строкового литерала, вам нужно написать его в виде списка chars

Да, C++ 11 constexpr сделаю это для вас:

 constexpr int LiteralToInt(const char * str) {
      return __COUNTER__; // or whatever.
 }

Примерно так будет работать

extern const char HELLO[] = "Hello";

а потом

cout << LiteralToInt<HELLO>();

но не сам буквальный. Это, вероятно, не то, что вы хотите.

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

Немного подумав об ответе Ричарда Дж. Росса III с использованием constexpr, я получил правильный ключ для поиска... Что вы по сути делаете, это хэширование строки во время компиляции. Вы можете сделать это в C++11 (но не в более ранних версиях), как показано здесь.

Основная идея состоит в том, чтобы использовать что-то вроде этого:

unsigned int constexpr const_hash(char const *input) { 
    // really simple hash function...
    return static_cast<unsigned int>(*input) 
         && static_cast<unsigned int>(*input) + hash(input+1); 
}

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

Однако, если вы не используете C++ 11, то мое предыдущее утверждение верно:

Нет - во время компиляции невозможно преобразовать строковые литералы в целые числа таким образом, чтобы все одинаковые строки отображались в одинаковые значения (и разные строки отображались в разные значения) во всех единицах компиляции, если не обрабатывать код каким-то образом.

Другие вопросы по тегам