Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在switch语句中使用字符串-使用C++;17?_C++_Switch Statement_Stdstring_C++17_String Literals - Fatal编程技术网

C++ 在switch语句中使用字符串-使用C++;17?

C++ 在switch语句中使用字符串-使用C++;17?,c++,switch-statement,stdstring,c++17,string-literals,C++,Switch Statement,Stdstring,C++17,String Literals,我们每个人(可能)都有过童年的写作梦想: switch(my_std_string) { case "foo": do_stuff(); break; case "bar": do_other_stuff(); break; default: just_give_up(); } 但这是不可能的,正如过去(2009年)对这个问题的回答所解释的: 从那时起,我们看到了C++11的出现,它让我们可以做到: switch (my_hash::hash(my_std_string)) {

我们每个人(可能)都有过童年的写作梦想:

switch(my_std_string) {
case "foo":  do_stuff(); break;
case "bar":  do_other_stuff(); break;
default:     just_give_up();
}
但这是不可能的,正如过去(2009年)对这个问题的回答所解释的:

从那时起,我们看到了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();
}
正如to中所描述的——虽然它实际上并没有达到我们想要的效果,但也不是那么糟糕——存在碰撞的可能性

我的问题是:从那时起,这种语言的发展(我想主要是C++14)是否影响了人们编写类似字符串大小写语句的方式?或简化螺母和螺栓以实现上述功能

具体地说,关于存在的结论——我对给出的答案感兴趣,因为我们可以假设标准将包含什么


注意:这不是关于使用switch语句优点的讨论,也不是关于meta的线程。我在问一个信息丰富的问题,所以请在此基础上回答/向上/向下投票。

写起来很容易

switcher(expr)->*
caser(case0)->*[&]{
}->*
caser(case1)->*[&]{
};
要通过
caseN
构建一个静态大小的
case0
哈希表,动态填充它,测试与
=
的冲突,通过
expr
进行查找,并运行相应的lambda

甚至可以支持
caser(case3)->*caser(case4)->*lambda
->*故障排除

我认为没有迫切的需要


我也不认为用C++17编写这篇文章有什么好处。

我的建议可以用C++14编写,但是用
if constexpr
std::string_view
编写起来比较省力

首先-我们需要constexpr字符串-如下所示:

template <char... c>
using ConstString = std::integer_sequence<char, c...>;

template <char ...c>
constexpr auto operator ""_cstr ()
{
    return  ConstString<c...>{};
}
下一步-定义开关箱代码:

template <typename Callable, typename Key>
class StringSwitchCase;

template <typename Callable, char ...c>
struct StringSwitchCase<Callable, ConstString<c...>>
{
    constexpr bool operator == (const std::string_view& str) // c++17 only
    {
        constexpr char val[] = {c..., '\0'};
        return val == str;
    }
    Callable call;
    static constexpr ConstString<c...> key{};
};

template <typename Callable, char ...c>
constexpr auto makeStringSwitchCase(CString<c...>, Callable call)
{
    return StringSwitchCase<Callable, ConstString<c...>>{call};
}
以及可能的用途:

StringSwitch cstrSwitch(   
    makeStringSwitchCase(234_cstr, 
                          [] { 
                              cout << "234\n"; 
                          }),
    makeStringSwitchCase(ConstString<'a', 'b', 'c'>{}, // only C++ standard committee know why I cannot write "abc"_cstr  
                          [] { 
                              cout << "abc\n"; 
                          }),
    makeStringSwitchDefaultCase([] { 
                              cout << "Default\n"; 
                          }));

cstrSwitch.call("abc"s);

对@PiotrNycz有趣的答案稍作修改,使语法更类似于“naive”开关,我们可以这样写:

switch_(my_std_string, 
case_(234_cstr, [] {     
    std::cout << "do stuff with the string \"234\" \n"; 
}),
case_(ConstString<'a', 'b', 'c'> { }, [] { 
    std::cout << "do other stuff with the string \"abc\"\n";
}),
default_( [] { 
    std::cout << "just give up.\n"; 
})      
开关(我的标准字符串,
案例(234)cstr,[]{

std::coutswitch
语句的最初原因是编译器可以将其映射到类似的机器操作。对于具有大量事例的开关,这会生成非常高效的机器代码

对于字符串,由于需要比较,这是不可能的,因此实现效率将远远低;与IF/EFR/FIF子句没有任何区别。C和C++语言家族仍然有目标,可以在没有任何开销的情况下产生非常高效的机器代码,因此字符串的切换不是U形的。seful扩展——如果你真的需要更高效的话,有更有效的方法来编写代码。这也意味着在语言语法中添加一个“strcmp”,以及它的所有变体和变化莫测的地方——这不是一个好主意


<> P> >对于任何版本的C++,我怀疑这是一个很好的扩展。

< P>这是一个简单的解决C/C++中切换实例的解决方案。 更新:包括continue版本。早期版本不能在循环中使用continue语句。当在循环中使用常规的switch-case块时,它可以按预期执行continue。但是,由于我们在switch-case宏中使用for-loop,continue只会带出switch-case块,而不会带出循环,在循环中它是u塞德

以下是要使用的宏:

#ifndef SWITCH_CASE_INIT
#define SWITCH_CASE_INIT
    char __switch_continue__;

    #define SWITCH(X)   __switch_continue__=0; \
                    for (char* __switch_p__=X, __switch_next__=1; __switch_p__!=0 ; __switch_next__=2) { \
                        if (__switch_next__==2) { __switch_continue__=1; break;
    #define CASE(X)         } if (!__switch_next__ || !(__switch_next__ = strcmp(__switch_p__, X))) {
    #define DEFAULT         } {
    #define END         __switch_p__=0; }}
    #define CONTINUE    __switch_p__=0; }} if (__switch_continue__) { continue; }
#endif
示例:带continue的SWITCH-CASE

如果在循环中使用开关块,而我们恰好在开关中使用continue,则需要使用continue(而不是end)结束开关

  • 需要使用SWITCH..CASE..CONTINUE在循环中(如果在开关中需要CONTINUE,也需要使用CONTINUE)

  • 需要使用开关..案例..默认情况下结束

  • 可以使用反向字符串比较

    开关(“abc”)外壳(str1)端

这种比较可以打开大量的比较选项,避免笨拙的if-else链。如果没有逐个字符的比较,就无法进行字符串比较,因此也无法避免if-else链。至少使用SWITCH-CASE的代码看起来很可爱。但瓶颈是它使用了

  • 3个额外变量
  • 5额外作业和
  • 每种情况下1个额外(bool)比较

因此,itz对开发人员在if-else和SWITCH-CASE之间进行选择的自由裁量权给出了另一种解决方案。但这个版本也使用了一系列比较

  • 3个赋值(包括所有大小写字符串指针的数组)
  • 字符串比较,直到找到匹配项为止
  • 在整数上递增,直到找到匹配项

由于C++11,您可以使用(参见-特别是2016年更新):

#包括“cttrie.h”
...
常量字符*str=。。。;
特里亚(str)

晚会晚了,这里有一个我不久前提出的解决方案,它完全符合所要求的语法,也适用于c++11

#include <uberswitch/uberswitch.hpp>

uberswitch(my_std_string) {
case ("foo"):  do_stuff(); break;
case ("bar"):  do_other_stuff(); break;
default:       just_give_up();
}
#包括
uberswitch(我的标准字符串){
案例(“foo”):dou_stuff();break;
箱子(“吧台”):不要做其他东西;打破;
默认设置:只需放弃();
}
需要注意的唯一区别是使用
uberswitch
代替
switch
,以及
大小写
值周围的括号(需要,因为这是一个宏)


代码是这样的:

我不能说我曾经做过这样的梦。您给出的哈希示例有一个错误命中的非零概率(即两个不相等的字符串有一个相等哈希的非零概率)。考虑到
开关的起源
(即,利用允许基于整数值构造跳转表的机器指令)如果C++的任何版本都支持非整数的开关/ CASE,我会感到惊讶。同意,@彼得,<代码>哈希< /C>解决方案不是正式正确的…除非,也许-额外的检查是为了验证等式,所以没有明确的好处。OTHH,在语言中有一个字符串的切换,什么是BeNFF?
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/comma_if.hpp>

#define ELEMENT_OR_NULL(z, n, text) BOOST_PP_COMMA_IF(n) (n < sizeof(text)) ? text[n] : 0
#define CONST_STRING(value) typename ExpandConstString<ConstString<BOOST_PP_REPEAT(20, ELEMENT_OR_NULL, #value)>, \
                                                       ConstString<>, sizeof(#value) - 1>::type

template <typename S, typename R, int N>
struct ExpandConstString;
template <char S1, char ...S, char ...R, int N>
struct ExpandConstString<ConstString<S1, S...>, ConstString<R...>, N> :
       ExpandConstString<ConstString<S...>, ConstString<R..., S1>, N - 1>
{};
template <char S1, char ...S, char ...R>
struct ExpandConstString<ConstString<S1, S...>, ConstString<R...>, 0>
{
    using type = ConstString<R...>;
};
int main() {
    StringSwitch cstrSwitch(
        makeStringSwitchCase(CONST_STRING(234){}, 
                              [] { 
                                  cout << "234\n"; 
                              }),
        makeStringSwitchCase(CONST_STRING(abc){}, 
                              [] { 
                                  cout << "abc\n"; 
                              }),
        makeStringSwitchDefaultCase([] { 
                                  cout << "Default\n"; 
                              }));

    cstrSwitch.call("abc"s);
}
switch_(my_std_string, 
case_(234_cstr, [] {     
    std::cout << "do stuff with the string \"234\" \n"; 
}),
case_(ConstString<'a', 'b', 'c'> { }, [] { 
    std::cout << "do other stuff with the string \"abc\"\n";
}),
default_( [] { 
    std::cout << "just give up.\n"; 
})      
#include <iostream>
#include <array>
#include <tuple>
#include <string>
#include <type_traits>
#include <utility>


template<char ... c>
using ConstString = std::integer_sequence<char, c...>;

template <char ...c>
constexpr auto operator ""_cstr ()
{
    return ConstString<c...> {};
}

template<char ... c1, char ...c2>
constexpr bool operator == (ConstString<c1...>, ConstString<c2...>) 
{
    if constexpr (sizeof...(c1) == sizeof...(c2)) {
        return std::tuple {c1...} == std::tuple {c2...};
    }
    else { return false; }
}

template<typename Callable, typename Key>
class SwitchCase;

template<typename Callable, char ...c>
struct SwitchCase<Callable, ConstString<c...>> {
    constexpr bool operator == (const std::string_view& str) {
        constexpr char val[] = { c..., '\0' };
        return val == str;
    }
    const ConstString<c...> key;
    Callable call;
};

template<typename Callable, char ...c>
constexpr auto case_(ConstString<c...> key, Callable call) 
{
    return SwitchCase<Callable, ConstString<c...>> { key, call };
}

template<typename Callable>
struct SwitchDefaultCase {
    constexpr bool operator == (const std::string_view&) { return true; }
    Callable call;
};

template<typename Callable>
constexpr auto default_(Callable call) 
{
    return SwitchDefaultCase<Callable> { call };
}

template<typename ...Cases>
class switch_ {
public:
    // I thought of leaving this enabled, but it clashes with the second ctor somehow
    // switch_(Cases&&... cases) : cases(std::forward<Cases>(cases)...) {}

    constexpr auto call(const std::string_view& str) {
        return call<0u>(str);
    }

    switch_(const std::string_view&& str, Cases&&... cases) :
            cases(std::forward<Cases>(cases)...) {
        call<0u>(str);
    }

private:
    template<std::size_t idx>
    constexpr auto call(const std::string_view& str) {
        if constexpr (idx < sizeof...(Cases)) {
            if (std::get<idx>(cases) == str) {
                return std::get<idx>(cases).call();
            }
            return call<idx + 1>(str);
        }
        else { return; }
    }

    std::tuple<Cases...> cases;
};

int main() {
    std::string my_std_string("abc");
    std::cout << "What is \"" << my_std_string << "\"?\n";

    switch_(my_std_string, 
    case_(234_cstr, [] {     
        std::cout << "do stuff\n"; 
    }),
    case_(ConstString<'a', 'b', 'c'> { }, [] { 
        std::cout << "do other stuff\n";
    }),
    default_( [] { 
        std::cout << "just give up\n"; 
    })      
    );
}
#ifndef SWITCH_CASE_INIT
#define SWITCH_CASE_INIT
    char __switch_continue__;

    #define SWITCH(X)   __switch_continue__=0; \
                    for (char* __switch_p__=X, __switch_next__=1; __switch_p__!=0 ; __switch_next__=2) { \
                        if (__switch_next__==2) { __switch_continue__=1; break;
    #define CASE(X)         } if (!__switch_next__ || !(__switch_next__ = strcmp(__switch_p__, X))) {
    #define DEFAULT         } {
    #define END         __switch_p__=0; }}
    #define CONTINUE    __switch_p__=0; }} if (__switch_continue__) { continue; }
#endif
#include <stdio.h>
#include <string.h>

#ifndef SWITCH_CASE_INIT
#define SWITCH_CASE_INIT
    char __switch_continue__;

    #define SWITCH(X)   __switch_continue__=0; \
                        for (char* __switch_p__=X, __switch_next__=1; __switch_p__!=0 ; __switch_next__=2) { \
                            if (__switch_next__==2) { __switch_continue__=1; break;
    #define CASE(X)         } if (!__switch_next__ || !(__switch_next__ = strcmp(__switch_p__, X))) {
    #define DEFAULT         } {
    #define END         __switch_p__=0; }}
    #define CONTINUE    __switch_p__=0; }} if (__switch_continue__) { continue; }
#endif


int main()
{
    char* str = "def";
    char* str1 = "xyz";

    while (1) {
        SWITCH (str)
            CASE ("abc")
                printf ("in abc\n");
                break;

            CASE ("def")                                
                printf("in def (continuing)\n");
                str = "ghi";
                continue;                               // <== Notice: Usage of continue (back to enclosing while loop)

            CASE ("ghi")                                // <== Notice: break; not given for this case. Rolls over to subsequent CASEs through DEFAULT
                printf ("in ghi (not breaking)\n");

            DEFAULT
                printf("in DEFAULT\n");

        CONTINUE                                        // <== Notice: Need to end the SWITCH with CONTINUE

        break; // break while(1)
    }
}
in def (continuing)
in ghi (not breaking)
in DEFAULT
#include <stdio.h>
#include <string.h>

#define SWITCH(X, ...) \
            char * __switch_case_ ## X ## _decl[] = {__VA_ARGS__}; \
            int __switch_case_ ## X ## _i=0, __switch_case_ ## X ## _size = sizeof(__switch_case_ ## X ## _decl)/sizeof(char*); \
            while (__switch_case_ ## X ## _i < __switch_case_ ## X ## _size && strcmp(X, __switch_case_ ## X ## _decl[__switch_case_ ## X ## _i])){ __switch_case_ ## X ## _i++; } \
            switch (__switch_case_ ## X ## _i)


int main()
{
    char * str = "def";

    SWITCH (str, "abc", "def", "ghi", "jkl")
    {
    case 0:
        printf (str);
        break;
    case 1:
        printf (str);
        break;
    case 2:
        printf (str);
        break;
    case 3:
        printf (str);
        break;
    default:
        printf ("default");
    }

    return 0;
}
def
#include "cttrie.h"
...
const char *str = ...;

  TRIE(str)
    std::cout << "Not found\n";
  CASE("abc")
    std::cout << "Found abc\n";
  CASE("bcd")
    std::cout << "Found bcd\n";
  ENDTRIE;
#include <uberswitch/uberswitch.hpp>

uberswitch(my_std_string) {
case ("foo"):  do_stuff(); break;
case ("bar"):  do_other_stuff(); break;
default:       just_give_up();
}