关于C ++中具有QString类型的qt:switch / case语句

switch/case statement in C++ with a QString type

我想在程序中使用大小写转换,但是编译器给我这个错误:

1
switch expression of type 'QString' is illegal

如何将switch语句与QString一起使用?

我的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
bool isStopWord( QString word )
{
bool flag = false ;

switch( word )
{
case"the":
    flag = true ;
    break ;
case"at" :
    flag = true ;
    break ;
case"in" :
    flag = true ;
    break ;
case"your":
    flag = true ;
    break ;
case"near":
    flag = true ;
    break ;
case"all":
    flag = true ;
    break ;
case"this":
    flag = true ;
    break ;
}

return flag ;
}


How can I use the switch statement with a QString?

你不能在C ++语言中,switch语句只能与整数或枚举类型一起使用。您可以将类类型的对象正式放入switch语句中,但这仅意味着编译器将寻找用户定义的转换以将其转换为整数或枚举类型。


您可以在迭代之前创建一个QStringList,如下所示:

1
2
3
4
5
6
7
8
QStringList myOptions;
myOptions <<"goLogin" <<"goAway" <<"goRegister";

/*
goLogin = 0
goAway = 1
goRegister = 2
*/

然后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
switch(myOptions.indexOf("goRegister")){
  case 0:
    // go to login...
    break;

  case 1:
    // go away...
    break;

  case 2:
    //Go to Register...
    break;

  default:
    ...
    break;
}


在C ++中不可能直接在字符串上切换。但是,可以在Qt中使用QMetaEnum,如下所示:Q_ENUM以及如何打开字符串

为此,首先在类声明中声明一个枚举,其中包含要在切换案例中使用的字符串作为枚举器名称。然后使用Q_ENUMS将枚举添加到元数据中,以便以后搜索程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <QMetaEnum>

class TestCase : public QObject
{
    Q_OBJECT
    Q_ENUMS(Cases)        // metadata declaration

public:
    explicit Test(QObject *parent = 0);

    enum Cases
    {
        THE, AT, IN, THIS // ... ==> strings to search, case sensitive
    };

public slots:
    void SwitchString(QString word);
};

然后,在.cpp文件中,使用将字符串转换为相应的值后,实现所需的开关。

比较是区分大小写的,因此,如果要进行不区分大小写的搜索,请首先将输入字符串转换为大写/小写。您还可以对字符串进行其他所需的转换。例如,如果您需要在C / C ++标识符中切换带有空格或不允许的字符的字符串,则可以转换/删除/替换这些字符以使字符串成为有效的标识符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void TestCase::SwitchString(QString word)
{
    // get information about the enum named"Cases"
    QMetaObject MetaObject = this->staticMetaObject;
    QMetaEnum MetaEnum = MetaObject.enumerator(MetaObject.indexOfEnumerator("Cases"));

    switch (MetaEnum.keyToValue(word.toUpper().toLatin1()))
    // or simply switch (MetaEnum.keyToValue(word)) if no string modification is needed
    {
        case THE:  /* do something */ break;
        case AT:   /* do something */ break;
        case IN:   /* do something */ break;
        case THIS: /* do something */ break;
        default:   /* do something */ break;
    }
}

然后只需使用该类来切换字符串。例如:

1
2
3
4
TestCase test;
test.SwitchString("At");
test.SwitchString("the");
test.SwitchString("aBCdxx");


@DomTomCat的答案已经涉及到此问题,但是由于该问题专门询问Qt,因此有更好的方法。

Qt已经具有用于QStrings的哈希函数,但是不幸的是Qt4的qHash没有资格作为constexpr。幸运的是,Qt是开源的,因此我们可以将QStrings的qHash功能复制到我们自己的constexpr哈希函数中并使用它!

Qt4的qHash源

我将其修改为仅需要一个参数(字符串文字始终以null终止):

1
2
3
4
5
6
7
8
9
10
11
12
uint constexpr qConstHash(const char *string)
{
    uint h = 0;

    while (*string != 0)
    {
        h = (h << 4) + *string++;
        h ^= (h & 0xf0000000) >> 23;
        h &= 0x0fffffff;
    }
    return h;
}

一旦定义了它,就可以在switch语句中使用它,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
QString string;
// Populate the QString somehow.

switch (qHash(string))
{
    case qConstHash("a"):
        // Do something.
        break;
    case qConstHash("b"):
        // Do something else.
        break;
}

由于此方法使用Qt计算哈希的相同代码,因此它具有与QHash相同的抗哈希冲突性,通常这是非常好的。缺点是这需要一个相当新的编译器-因为它在constexpr哈希函数中具有不可返回的语句,因此它需要C ++ 14。


如果可以使用现代C ++编译器,则可以为字符串计算编译时哈希值。在此答案中,有一个非常简单的constexpr哈希函数的示例。

因此,解决方案如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// function from https://stackoverflow.com/a/2112111/1150303
// (or use some other constexpr hash functions from this thread)
unsigned constexpr const_hash(char const *input) {
    return *input ?
    static_cast<unsigned int>(*input) + 33 * const_hash(input + 1) :
    5381;
}

QString switchStr ="...";
switch(const_hash(switchStr.toStdString().c_str()))
{
case const_hash("Test"):
    qDebug() <<"Test triggered";
    break;
case const_hash("asdf"):
    qDebug() <<"asdf triggered";
    break;
default:
    qDebug() <<"nothing found";
    break;
}

它仍然不是一个完美的解决方案。可能存在哈希冲突(因此,每次添加/更改case时都要测试程序),例如,如果要使用奇异字符或utf字符,则必须小心地将其从QString转换为char*

对于c ++ 11,对于Qt5,将CONFIG += c++11添加到项目中。 Qt4:QMAKE_CXXFLAGS += -std=c++11


尝试这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// file qsswitch.h
#ifndef QSSWITCH_H
#define QSSWITCH_H

#define QSSWITCH(__switch_value__, __switch_cases__) do{\
    const QString& ___switch_value___(__switch_value__);\
    {__switch_cases__}\
    }while(0);\

#define QSCASE(__str__, __whattodo__)\
    if(___switch_value___ == __str__)\
    {\
    __whattodo__\
    break;\
    }\

#define QSDEFAULT(__whattodo__)\
    {__whattodo__}\

#endif // QSSWITCH_H

如何使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include"qsswitch.h"

QString sW1 ="widget1";
QString sW2 ="widget2";

class WidgetDerived1 : public QWidget
{...};

class WidgetDerived2 : public QWidget
{...};

QWidget* defaultWidget(QWidget* parent)
{
    return new QWidget(...);
}

QWidget* NewWidget(const QString &widgetName, QWidget *parent) const
{
    QSSWITCH(widgetName,
             QSCASE(sW1,
             {
                 return new WidgetDerived1(parent);
             })
             QSCASE(sW2,
             {
                 return new WidgetDerived2(parent);
             })
             QSDEFAULT(
             {
                 return defaultWidget(parent);
             })
             )
}

有一些简单的宏魔术。预处理后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
QSSWITCH(widgetName,
         QSCASE(sW1,
         {
             return new WidgetDerived1(parent);
         })
         QSCASE(sW2,
         {
             return new WidgetDerived2(parent);
         })
         QSDEFAULT(
         {
             return defaultWidget(parent);
         })
         )

将像这样工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// QSSWITCH
do{
        const QString& ___switch_value___(widgetName);
        // QSCASE 1
        if(___switch_value___ == sW1)
        {
            return new WidgetDerived1(parent);
            break;
        }

        // QSCASE 2
        if(___switch_value___ == sW2)
        {
            return new WidgetDerived2(parent);
            break;
        }

        // QSDEFAULT
        return defaultWidget(parent);
}while(0);


如前所述,这不是Qt问题,switch语句只能使用常量表达式,查看集合类QSet是一个很好的解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
void initStopQwords(QSet<QString>& stopSet)
{
    // Ideally you want to read these from a file
    stopSet <<"the";
    stopSet <<"at";
    ...

}

bool isStopWord(const QSet<QString>& stopSet, const QString& word)
{
    return stopSet.contains(word);
}


我建议使用if和break。这将使得在计算中接近切换大小写。

1
2
3
4
5
6
7
8
9
10
11
QString a="one"
if (a.contains("one"))
{

   break;
}
if (a.contains("two"))
{

   break;
}

恕我直言,这似乎有些理智。

1
2
3
4
5
6
7
8
9
10
11
bool isStopWord( QString w ) {
    return (
        w =="the" ||
        w =="at" ||
        w =="in" ||
        w =="your" ||
        w =="near" ||
        w =="all" ||
        w =="this"
    );
}


1
2
case"the":
    //^^^ case label must lead to a constant expression

我不知道qt,但是您可以尝试一下。如果QString与普通的std::string没有区别,可以避免使用switch并直接使用==进行比较。

1
2
3
4
5
6
7
8
9
if( word =="the" )
{
   // ..
}
else if( word =="at" )
{
   // ..
}
// ....


看看这个,对我有帮助

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int main(int, char **)
{
    static const uint red_hash = 30900;
    static const uint green_hash = 7244734;
    static const uint blue_hash = 431029;
  else  
    static const uint red_hash = 112785;  
    static const uint green_hash = 98619139;  
    static const uint blue_hash = 3027034;
  endif

    QTextStream in(stdin), out(stdout);
    out <<"Enter color:" << flush;
    const QString color = in.readLine();
    out <<"Hash=" << qHash(color) << endl;

    QString answer;
    switch (qHash(color)) {
    case red_hash:
        answer="Chose red";
        break;
    case green_hash:
        answer="Chose green";
        break;
    case blue_hash:
        answer="Chose blue";
        break;
    default:
        answer="Chose something else";
        break;
    }
    out << answer << endl;
}


这与Qt无关,就像它与袜子的颜色无关。

C ++开关语法如下:

1
2
3
4
5
6
7
8
9
10
11
char c = getc();
switch( c ) {
case 'a':
    a();
    break;
case 'b':
    b();
    break;
default:
    neither();
}

如果那没有帮助,请详细列出错误消息,以及袜子的颜色。

编辑:要回复您的答复,您不能将switch与非整数类型一起使用。特别是,您不能使用类类型。不是QString类型的对象,也不是任何其他类型的对象。您可以改用if-else if-else构造,也可以使用运行时或编译时多态性,重载或switch的任何替代数组。


它与C ++ switch语句相同。

1
2
3
4
5
6
7
8
switch(var){
  case(option1):
      doesStuff();
      break;
  case(option2):
     etc();
     break;
}