【发布时间】:2019-12-03 11:12:44
【问题描述】:
假设您有一些哈希值,并希望在编译时将它们映射到各自的字符串。 理想情况下,我希望能够写出以下内容:
constexpr std::map<int, std::string> map = { {1, "1"}, {2 ,"2"} };
不幸的是,这在 C++17 和 C++2a 中都是不可能的。尽管如此,
我尝试使用std::array 来模拟它,但无法在编译时获取初始化列表的大小,以便在没有明确指定大小的情况下正确设置数组的类型。
这是我的模型:
template<typename T0, typename T1>
struct cxpair
{
using first_type = T0;
using second_type = T1;
// interestingly, we can't just = default for some reason...
constexpr cxpair()
: first(), second()
{ }
constexpr cxpair(first_type&& first, second_type&& second)
: first(first), second(second)
{ }
// std::pair doesn't have these as constexpr
constexpr cxpair& operator=(cxpair<T0, T1>&& other)
{ first = other.first; second = other.second; return *this; }
constexpr cxpair& operator=(const cxpair<T0, T1>& other)
{ first = other.first; second = other.second; return *this; }
T0 first;
T1 second;
};
template<typename Key, typename Value, std::size_t Size = 2>
struct map
{
using key_type = Key;
using mapped_type = Value;
using value_type = cxpair<Key, Value>;
constexpr map(std::initializer_list<value_type> list)
: map(list.begin(), list.end())
{ }
template<typename Itr>
constexpr map(Itr begin, const Itr &end)
{
std::size_t size = 0;
while (begin != end) {
if (size >= Size) {
throw std::range_error("Index past end of internal data size");
} else {
auto& v = data[size++];
v = std::move(*begin);
}
++begin;
}
}
// ... useful utility methods omitted
private:
std::array<value_type, Size> data;
// for the utilities, it makes sense to also have a size member, omitted for brevity
};
现在,如果您只是使用普通的 std::array 来实现,那么开箱即用:
constexpr std::array<cxpair<int, std::string_view>, 2> mapp = {{ {1, "1"}, {2, "2"} }};
// even with plain pair
constexpr std::array<std::pair<int, std::string_view>, 2> mapp = {{ {1, "1"}, {2, "2"} }};
不幸的是,我们必须明确给出数组的大小作为第二个模板参数。这正是我想要避免的。 为此,我尝试构建您在上面看到的地图。 有了这个伙伴,我们可以编写如下内容:
constexpr map<int, std::string_view> mapq = { {1, "1"} };
constexpr map<int, std::string_view> mapq = { {1, "1"}, {2, "2"} };
不幸的是,一旦我们超过了 map 中的魔术 Size 常量,我们就会得到一个错误,所以我们需要明确地给出大小:
//// I want this to work without additional shenanigans:
//constexpr map<int, std::string_view> mapq = { {1, "1"}, {2, "2"}, {3, "3"} };
constexpr map<int, std::string_view, 3> mapq = { {1, "1"}, {2, "2"}, {3, "3"} };
当然,只要你在 constexpr 范围内throw,你就会得到一个编译错误,并且可以显式地调整魔法常数。但是,这是我想隐藏的实现细节。用户不需要处理这些低级细节,这是编译器应该推断的。
不幸的是,我没有看到具有确切语法 map = { ... } 的解决方案。我什至看不到constexpr auto map = make_map({ ... }); 之类的东西。此外,这是一个与运行时不同的 API,我想避免使用它以增加易用性。
那么,是否有可能在编译时从初始化列表中推断出这个大小参数?
【问题讨论】:
-
你显示的地图初始化错误,你应该切换整数和字符串。
-
听起来有点像X-Y problem。您想获得已知值集的快速反向散列吗?你真的需要吗?你的实际问题是什么?
-
另外,请向我们展示您对
std::array的尝试 -
我已经相应地编辑了我的问题。如果还有什么不清楚的,请告诉我。
-
@JHBonarius 对字符串字面量很好