您好,我有一个非常具体的问题,我已经尝试解决这个问题几个小时了。
我有一个关键类,能够将字符串存储为模板参数。我想用它来使用
key_index<KEY_TYPE>::get_index()
有效地从此类键获取索引。但是,我在从字符串创建键时遇到了问题。尽管事实上 str
参数在编译时是已知的,但编译器只是不想让我将它用作模板参数。
有人知道解决方法吗?或者这完全是胡说八道,我应该把它扔到大海里并使用 std::unordered_map 代替?
PS:我正在 Visual Studio 中使用 MSVC++,尚未在其他编译器中尝试过,但老实说,我认为它的行为不会有所不同。
#include <iostream>
#include <type_traits>
#include <array>
#include <memory>
#include <stdio.h>
// An array of characters with a fixed size
template <std::size_t N>
using str_ct = std::array<char, N>;
// A key type that holds a string literal
template <char... Chars>
struct key {
using _is_key = void;
static constexpr str_ct<sizeof...(Chars) + 1> value = { Chars..., '\0' };
};
// A concept to check if a type is a key
template <class T>
concept key_concept = requires {
typename T::_is_key;
};
// Global index for keys
struct key_index_base {
protected:
static std::size_t index;
};
std::size_t key_index_base::index = 0;
// A type that holds the index of a key
template <key_concept KEY>
struct key_index : public key_index_base {
static std::size_t get_index() {
static std::size_t index = key_index_base::index++;
return index;
}
};
// Specialization for non-empty string literals
template <size_t N, size_t I, char... Chars>
struct string_to_key_helper {
static consteval auto convert(const char(&str)[N]) {
if constexpr (I < N - 1) // -1 to exclude the null terminator
return string_to_key_helper<N, I + 1, Chars..., str[I]>::convert(str);
else
return key<Chars...>{};
}
};
// Helper function to deduce the array size and start the conversion
template <std::size_t N>
constexpr auto make_key(const char(&str)[N]) {
return string_to_key_helper<N, 0>::convert(str);
}
int main() {
constexpr auto k = make_key("Hello, World!");
for (auto c : k.value) {
std::cout << (int)c << " ";
}
}
Consteval 函数参数不是函数内的常量表达式:https://stackoverflow.com/a/56131418/5754656
函数参数的值从来都不是常量表达式,因此您会在
str[I]
中得到 string_to_key_helper<N, I + 1, Chars..., str[I]>
的错误。
你可以让
key<"string literal">
工作:
https://godbolt.org/z/vjKWoca17
template <std::size_t N>
struct CString {
consteval CString(const char(& s)[N]) {
for (std::size_t i = 0; i < N; ++i) {
value[i] = s[i];
}
if (s[N - 1u]) {
throw "CString must be NUL-terminated like a string literal";
}
}
char value[N];
constexpr operator std::string_view() const noexcept {
return { value, value + N - 1u };
}
};
template <CString S>
struct key {
using _is_key = void;
static constexpr const char(& value)[std::size(S.value)] = S.value;
};
int main() {
constexpr auto k = key<"Hello, world!">();
for (char c : k.value) {
std::cout << int{c} << ' ';
}
std::cout << '\n';
}