我们每个人都有(可能)童年的写作梦想:
switch(my_std_string) {
case "foo": do_stuff(); break;
case "bar": do_other_stuff(); break;
default: just_give_up();
}
但这是不可能的,正如《奥登时代》(2009年)对这个问题的回答所解释的那样:
Why the switch statement cannot be applied on strings?
从那时起,我们看到了C++11的出现,它让我们走得更远:
switch (my_hash::hash(my_std_string)) {
case "foo"_hash: do_stuff(); break;
case "bar"_hash: do_other_stuff(); break;
default: just_give_up();
}
正如answer到Compile time string hashing中所描述的-这并不那么糟糕,尽管它实际上并没有完全按照我们的要求做-有可能发生冲突。
我的问题是:从那时起,语言的发展(我想主要是C++14)影响了人们编写某种字符串case语句的方式吗?或者简化了实现上述目标的螺母和螺栓?
具体来说,随着C++17 standard的结论是just around the corner-我感兴趣的答案,我们可以假设标准将包含什么。
9条答案
按热度按时间fdbelqdn1#
我的建议在C++14上是可行的,但是在
if constexpr
和std::string_view
上写起来稍微容易一些。首先-我们需要constexpr字符串-像这样:
使用
tuple
的无模板构造以及tuple
现在具有constexproperator ==
的事实,operator ==
也更容易编写:下一件事-定义开关情况代码:
还需要默认情况:
所以,
StringSwitch
-实际上,它是if () {} else if () {} ... else {}
结构:可能的用法:
工作demo。
基于这个post,我设法以更简单的方式来处理ConstString。工作demo2。
增加部分如下:
通过改变
BOOST_PP_REPEAT(20, ELEMENT_OR_NULL, #value)
中的第一个参数(20
),我们可以控制ConstString
的最大可能大小-用法如下:6yt4nkrj2#
写起来很容易
构建一个静态大小的
case0
到caseN
的哈希表,动态填充它,测试与==
的冲突,通过expr
进行查找,并运行相应的lambda。甚至可以支持
caser(case3)->*caser(case4)->*lambda
和->*fallthrough
。我看不出有什么迫切的需要。
我也不认为用C++17写这个有什么好处。
lbsnaicq3#
从C++11开始,你可以使用smilingthax/cttrie(cf. C/C++: switch for non-integers - esp. 2016年更新):
在内部,Trie在编译时创建并存储为类型。在运行时,根据
str
遍历它。代码块被 Package 在lambda表达式中,并在相应的叶子上执行。jchrr9hc4#
这里是一个简单的解决方案,用于在C/C++中模拟开关情况。
**更新:**包括 * 继续 * 版本。早期版本不能在循环中使用 continue 语句。当在循环中使用时,常规的switch-case块可以执行 continue,正如预期的那样。但是因为我们在SWITCH-CASE宏中使用了 for 循环,所以 continue 只会带出SWITCH-CASE块,而不会带出使用它的循环。
下面是要使用的宏:
示例:带有 continue的开关箱
EXECUTE
如果SWITCH块在循环中使用,并且我们碰巧在SWITCH中使用continue,那么我们需要用CONTINUE(而不是END)结束SWITCH
输出:
SWITCH(“abc”)CASE(str1)END
这种比较可以打开大量的比较选项,并避免笨拙的if-else链。如果不逐个字符比较,则无法进行字符串比较,因此无法避免if-else链。至少SWITCH-CASE的代码看起来很可爱。但瓶颈在于
因此,开发人员在if-else和SWITCH-CASE之间选择的自由裁量权
fbcarpbf5#
对@PiotrNycz有趣的答案做了一个小小的修改,使语法更类似于'naive'开关,允许我们这样写:
全面实施:
和一个类似的working demo。现在我们真正需要的是从“abcd”类型的文字构造ConstStrings。
ukdjmx9f6#
使用
switch
语句的最初原因是编译器可以将其Map到类似的机器操作。对于具有大量案例的开关,这产生非常有效的机器代码。对于字符串,由于需要比较,这是不可能的,因此实现的效率要低得多;与if/else/else-if子句没有任何不同。C和 C++ 家族的目标仍然是允许在没有任何开销的情况下生成非常高效的机器代码,所以字符串上的开关不是一个有用的扩展-如果你真的需要更高效的话,有更有效的方法来编码。这还意味着在语言语法中添加一个“strcmp”,以及它的所有变化和变幻莫测--这不是一个好主意。
我怀疑这在任何时候对任何版本的C++来说都是一个很好的扩展。
tyu7yeag7#
这里有另一个解决方案。但这个版本也使用了一系列的比较。
DEMO
输出:
2q5ifsrm8#
在C++17中,我利用
std::find
作为<algorithm>
组的一部分。其思想是将所有case值字符串一起保存在一个可搜索的容器中(例如std::vector
)。我们将尝试定位正在搜索的字符串,然后根据找到的int
索引进行切换。因此,让我们开始创建一个finder模板,例如:
如果在haystack中没有找到指针,
find_case
将返回**-1
**,如果找到指针,则返回非负索引。以下是使用示例:
作为模板的一个优点是我们还可以使用其他类型,例如
std::wstring
。现在,让我们看看索引上的开关:
不要忘记包含
<vector>
和<algorithm>
头文件。在这个答案的范围之外,还有更强大的实现,使用
std
搜索器和std::any
,让我们在一个公共的switch
语句下同时拥有字符串和整数类型的情况。8ehkhllq9#
在聚会上,这里有一个我不久前提出的解决方案,它完全遵守所要求的语法,也适用于c++11。
需要注意的唯一区别是使用
uswitch
代替switch
,使用ucase
代替case
,并在值周围添加括号(需要,因为这是一个宏)。代码如下:https://github.com/falemagn/uberswitch