我有一个包含 3 个字段的结构,我想创建一个 lambda/函数指针的映射,其键是结构字段名称的字符串值的哈希值。 在编译时,使用断言检查映射:正确的长度 - 结构中的字段数,并且没有重复的键。 这适用于 long 作为键和值的映射,但不适用于 lambda 值。有没有办法让它在 C++17 上工作?
尝试这样做时,我收到错误:
错误:“constexpr”变量“ini”的类型“const std::pair
请参阅此处的示例: https://godbolt.org/z/3MjTq94rj 取消注释 MakeMapFunction
标题.hpp
struct Container {
int fieldA;
std::string fieldB;
bool fieldC;
};
constexpr inline long long int hash(char const *str, int h = 0)
{
return (!str[h] ? 5381 : (hash(str, h + 1) * 7) ^ str[h]);
}
template <typename T, typename V>
constexpr bool hasDuplicates(const std::pair<T, V> *array, std::size_t size)
{
for (std::size_t i = 1; i < size; i++)
{
for (std::size_t j = 0; j < i; j++)
{
if (array[i].first == array[j].first)
{
return 1;
}
}
}
return 0;
}
// Start of check number of fields in struct
template <typename T, typename... TArgs>
auto isAggregateConstructableImpl(std::tuple<TArgs...>) -> decltype(T{std::declval<TArgs>()...});
template <typename T, typename TArgs, typename = void>
struct isAggregateConstructable : std::false_type
{
};
template <typename T, typename TArgs>
struct isAggregateConstructable<T, TArgs, std::void_t<decltype(isAggregateConstructableImpl<T>(std::declval<TArgs>()))>> : std::true_type
{
};
// Checks if type can be initialized from braced-init-list.
template <typename T, typename TArgs>
constexpr auto isAggregateConstructableV = isAggregateConstructable<T, TArgs>::value;
// Class is convertible to anything.
class any
{
public:
template <typename T>
operator T() const;
};
template <class T, typename... TArgs>
constexpr std::size_t numBindingsImpl()
{
if constexpr (isAggregateConstructable<T, std::tuple<TArgs...>>())
{
return numBindingsImpl<T, any, TArgs...>();
}
else
{
return sizeof...(TArgs) - 1;
}
};
template <typename T>
constexpr auto structGetNumberOfFields = numBindingsImpl<T, any>();
class Holder
{
public:
static std::map<long long, long> MakeMapString();
static const std::map<long long, long> list;
};
源码.cpp
std::map<long long, long> Holder::MakeMapString()
{
constexpr std::pair<long long, long> ini[]{
{hash("fieldA"), 55}, // if removed then first static_assert will fail
{hash("fieldB"), 77}, // if changed to fieldA then second static_assert will fail
{hash("fieldC"), 99}};
// This assert determines if the length of ini is the same as number of fields in Container struct
static_assert(end(ini) - begin(ini) == structGetNumberOfFields<Container>, "[Container] has incorrect number of fields");
// This assert checks there are no duplicates in ini
static_assert(!hasDuplicates(ini, std::extent_v<std::remove_reference_t<decltype(ini)>>), "Duplicates Found!");
return {std::begin(ini), std::end(ini)};
}
const std::map<long long, long> listOf = Holder::MakeMapString();
您不能拥有
constexpr std::function
对象。
请记住,std::function
执行类型擦除和动态分配,并且尚不支持非瞬态分配。
换句话说,std::function
分配的任何内存都必须在常量表达式结束之前释放。
在您的示例中,您创建的 lambda 都没有捕获,因此您也可以将它们转换为函数指针:
// MakeMapFunction is pointless.
// Let's just use an immediately invoked lambda expression (IILE)
const std::map<long long, void(*)(int)> listOfFunction = [] {
static constexpr std::pair<long long, void(*)(int)> ini[] {
{hash("fieldA"), [](int a) {}},
{hash("fieldB"), [](int a) {}},
{hash("fieldC"), [](int a) {}}
};
// static assertions here ...
return {std::begin(ini), std::end(ini)};
}();
或者更简洁地说:
const std::map<long long, void(*)(int)> listOfFunction = {
{hash("fieldA"), [](int a) {}},
{hash("fieldB"), [](int a) {}},
{hash("fieldC"), [](int a) {}}
};
// static assertions here ...
顺便说一句,看到
std::map
的哈希值很奇怪。
您也可以使用std::unordered_map<std::string_view, void(*)(int)>
。