我正在尝试学习一些现代C++实践,例如模板,我决定创建一个天真而简单的命令行参数解析器,大部分工作都在编译时完成,但是我已经遇到了关于constexpr
的问题,本质上我想要做的就是在编译时检查重复条目(在运行时进行操作是微不足道的)。
首先,我有一个结构体来保存单个配置:
struct Arg_Opt_Tuple {
std::string_view mc{}; // multichar ie "help"
char sc{}; // singlechar ie 'h'
bool is_flag{};
};
现在假设我想创建一个函数(或最终是对象的构造函数),它返回一个固定大小的std::array,但同时在编译时还要检查重复或空值。我的目标是以类似以下方式调用它:
constexpr auto ARG_COUNT = 4U;
constexpr auto opts = checked_arr<ARG_COUNT>(
Arg_Opt_Tuple{"hello", 'h', false},
Arg_Opt_Tuple{"world", 'g', true},
Arg_Opt_Tuple{"goodbye", 'h', false}, // <- static_assert('h' == 'h')
Arg_Opt_Tuple{"hello", 'r', false} // <- static_assert(sv.compare("hello") == 0)
);
我的第一次尝试是使用std::initializer_list,但遇到了一些问题,在做了一些谷歌搜索后得出结论,它与constexpr在这里不是正确的选择。我目前的尝试涉及一个可变参数模板:
template <std::size_t N, typename... T>
constexpr std::array<Arg_Opt_Tuple, N> checked_arr(T... list) {
static_assert(N == sizeof...(T));
return {list...};
}
这个方法虽然可行,但与初始化一个数组完全多余,我真正希望它能在编译时进行一些检查。对于运行时的重复项或错误值很容易处理,只需循环比较或使用std::find等方式,但似乎没有一种方法可以在编译时实现。即使(我知道这很丑陋,但你懂我的意思):
for (std::size_t src_i = 0; src_i < ARG_COUNT; ++src_i) {
for (std::size_t check_i = 0; check_i < ARG_COUNT; ++check_i) {
// skip checking self
if (check_i == src_i) {
continue;
}
// doesnt work obviously
static_assert(opts[src_i].sc != opts[check_i].sc);
}
}
这个要实现起来有多难?这是一个糟糕的设计吗?如果有指导意见,那就太好了。