在结构列表上调用 std::sort 时将 const 作为 'this' 参数错误传递

Passing const as 'this' argument error when calling std::sort on a list of structures

我有一个相当大的 struct 叫做 Journey:

typedef struct Journey
{
    /**
      * @brief The InfoText struct is a container for storing messages (short and long) that are found
      * inside a journey's <InfoTextLists/> as <InfoText/> elements
      */
    typedef struct InfoText
    {
        QString shortMsg;
        QString longMsg;

        InfoText(QString shortMsg, QString longMsg)
        {
            this->shortMsg = shortMsg;
            this->longMsg = longMsg;
        }

        bool operator ==(const InfoText &other)
        {
            return (this->shortMsg == other.shortMsg) && (this->longMsg == other.longMsg);
        }

        bool operator !=(const InfoText &other)
        {
            return !(*this == other);
        }
    } InfoText;


    QTime arrivalNextImmediateStop;             //!< Arrival time (planned). It can be found under Time inside Arr in XML reply
    QTime arrivalNextImmediateStopRelative;     //!< Arrival time (relative, @p arrivalNextImmediateStop + @p delay)

    // Departure data
    QTime departure;            //!< Departure time (planned). It can be found under Time inside Dep in XML reply
    quint32 departureRelative;  //!< Departure time (relative and in minutes, @p departure + @p delay - current time)
    int delay;                  //!< Departure time delay (minutes). It can be found under Delay inside Dep in XML reply

    // Transfer time
    // TODO Calculate this at the beginning of the data fusion slotGetData(). It is required for the display of the connection (remaining time column)
    QTime transferTime;         //!< Transfer time based on the CSV L and V files. Required for the lookup filter (see @ref Data::Filtering::FilterFutureLookup)

    // Direction and destination data
    quint8 direction;           //!< Run direction (1, 2). It can be found under Attribute of type DIRECTIONFLAG in XML reply
    QString directionTarget;    //!< Run direction final destination. It can be found under Attribute of type DIRECTION in XML reply

    QString operatorJ;          //!< Full name of operator of the journey (Berliener Verkehrsbetriebe, DB Regio AG ...). It can be found under Attribute of type OPERATOR in XML reply
    QString vehicleType;        //!< Type of the vehicle (B, F, U, S ...). It can be found under Attribute with of type CATEGORY in XML reply
    QString line;               //!< The line of the vehicle (for example: 109 (for Bus 109), S5 (for S-Bahn 5) etc.). It can be found under Attribute of type NUMBER in XML reply

    // Immedidate stops data
    quint32 immediateStop1;     //!< First immediate stop ID (without offset) after current station
    quint32 immediateStop2;     //!< Second immediate stop ID (without offset) after current stations

    bool dirty;                 //!< Used by the secondary filtering mechanism this value shows whether the given connection has been marked for removal or not

    /**
     * @brief Stores all <InfoText/> element found inside the journey's <InfoTextLists/> element. An info text is a message consisting of a headline (short message)
     *        and a body (long message)
     */
    QList<InfoText> infoTexts;


    /**
     * @brief Constructor
     */
    Journey()
    {
        this->arrivalNextImmediateStop = QTime();
        this->arrivalNextImmediateStop = QTime();
        this->departure = QTime();
        this->departureRelative = 0;
        this->transferTime = QTime();
        this->delay = this->direction = this->immediateStop1 = this->immediateStop2 = 0;
        this->directionTarget = this->operatorJ = this->vehicleType = this->line = "";
        this->dirty = false;
    }

    /**
     * @brief Allows comparing for equality between two journeys
     * @param other Journey to compare with
     * @return True if arrival time to both next two immediate stops, departure time, relative departure
     *         time, delay, direction, destination, journey operator, vehicle type, line and IDs of next
     *         two immediate stops are equal
     */
    bool operator ==(const Journey &other)
    {
        return arrivalNextImmediateStop == other.arrivalNextImmediateStop
                && arrivalNextImmediateStopRelative == other.arrivalNextImmediateStopRelative
                && departure == other.departure
                && departureRelative == other.departureRelative
                && delay == other.delay
                && direction == other.direction
                && directionTarget == other.directionTarget
                && operatorJ == other.operatorJ
                && vehicleType == other.vehicleType
                && line == other.line
                && immediateStop1 == other.immediateStop1
                && immediateStop2 == other.immediateStop2;
    }

    /**
     * @brief Allows comparing for inequality between two journeys
     * @param other
     * @return True if arrival time to both next two immediate stops, departure time, relative departure
     *         time, delay, direction, destination, journey operator, vehicle type, line and IDs of next
     *         two immediate stops are not equal
     */
    bool operator !=(const Journey &other)
    {
        return !(*this == other);
    }

    /**
     * @brief Overloads the < operator to allow sorting journeys in a ascending order based on their line. Due to the alphanumeric nature of
     *        most lines special handling is required to ensure proper order. With the default string comparison namely own_line < other_line the result
     *        is in most cases not correct: M10, M5, M8 instead of M5, M8, M10. Using @ref alphanumericLineSplitRx an attempt is made to split the line
     *        into two distrinctive tokens - alphabetic and numeric. If the split does not succeed the line is made only of letter or digits in which case
     *        standard string comparison can be used. On the other hand if it does succeed, we need to do a comparison of each of the two tokens. In some
     *        cases lines can be the same in which case the direction of both is used for the comparison
     * @param other A journey with a line
     * @return True if line of journey is smaller (string comparison or numeric comparison if lines are of alphanumeric or only numeric nature) than line of @p journey
     */
    bool operator < (const Journey &other)
    {
        QRegularExpressionMatch matchesOwn = alphanumericLineSplitRx.match(this->line);
        QRegularExpressionMatch matchesOther = alphanumericLineSplitRx.match(other.line);

        // Figure out if the lines of our own and the other journey are complex (alphanumeric) or simple (just alphabetic or just numeric)
        // If we have alphanumeric lines we need to split each line into two tokens - alphabetic and numeric
        if (matchesOwn.capturedTexts().length() == 3 && matchesOther.capturedTexts().length() == 3)
        {
            QString lineAlphaOwn = matchesOwn.captured(1);
            QString lineAlphaOther = matchesOther.captured(1);
            quint16 lineNumericOwn = matchesOwn.captured(2).toUInt();
            quint16 lineNumericOther = matchesOther.captured(2).toUInt();

            // If the alphabetic token of both journies are different there is not need to compare the numeric token and
            // standard string comparison is used
            // Example: N20, M100 will be sorted as M100, N20 since M comes before N
            if (lineAlphaOwn != lineAlphaOther)
            {
                return this->line < other.line;
            }

            // If the alphabetic token is the same for both lines the numeric token is the next criterion for sorting
            // Example: N10, N1, N2, N20 will be sorted as N1, N2, N10, N20
            if (lineNumericOwn != lineNumericOther)
            {
                return lineNumericOwn < lineNumericOther;
            }

            // If both the alphabetic and the numeric tokens are the same the direction will be used as the sorting criterion
            // Example: N10 (direction 2), N10 (direction 1) will be sorted as N10 (direction 1), N10 (direction 2)
            return this->direction < other.direction;
        }

        // In case the matching has failed this means that the line consists either of just a single alphabetic or numeric
        // The numeric-only case needs to be handled to avoid sorting results like 1, 100, 1000, 2, 20, 2000 ...
        bool isOwnNumericOnly = false;
        bool isOtherNumericOnly = false;
        quint16 lineNumericOwn = matchesOwn.captured(1).toUInt(&isOwnNumericOnly);
        quint16 lineNumericOther = matchesOther.captured(1).toUInt(&isOtherNumericOnly);

        if (isOwnNumericOnly && isOtherNumericOnly)
        {
            // In case the line (digits only!) of both journies are different, we use standard numeric comparison
            // Example: 206, 815, 413 will be sorted as 206, 413, 815
            if (lineNumericOwn != lineNumericOther)
            {
                return lineNumericOwn < lineNumericOther;
            }

            // Both journies have the same number for a line so the direction is used as the sorting criterion
            // Example: 280 (direction 2), 280 (direction 1) will be sorted as 280 (direction 1), 280 (direction 2)
            return this->direction < other.direction;
        }
        else
        {
            // In case the line (letters only!) of both journies are different, we use standard string comparison
            // Example: S, TXL, R will be sorted as R, S, TXL
            if (this->line != other.line)
            {
                return this->line < other.line;
            }

            // Both journies have the same letter for a line so the direction is used as the sorting criterion
            // Example: TXL (direction 2), TXL (direction 1) will be sorted as TXL (direction 1), TXL (direction 2)
            return this->direction < other.direction;
        }
    }

    /**
     * @brief Overloads the > operator to allow sorting journeys in a ascending order based on their line. Internally the overloaded
     * @ref operator < and @ref operator != operators are used
     * @param other A journey with a line
     * @return True if line of journey is greater (string comparison or numeric comparison if lines are of alphanumeric or only numeric nature) than line of @p journey
     */
    bool operator > (const Journey &other)
    {
        return (*this != other) && (*this < other);
    }

    /**
     * @brief Checks if the journey belongs to a specific vehicle type
     * @param vehicleType Type of vehicle to check own affiliation with
     * @return True if journey belongs to @p vehicleType
     */
    bool belongsToVehicleType(QString vehicleType)
    {
        return this->vehicleType == vehicleType;
    }
} Journey;

它存储从服务器的 HTTP GET 回复中检索到的数据,该服务器提供有关各种车辆及其在城市中的路线的信息。

我正在使用的规范中的一项要求在我过滤了一次无效(基于各种标准)后导致了笨重的重载 < 运算符后,为旅程提供了一个不那么简单的排序机制(其他的在这里不重要)你在上面的代码片段中看到。

我不得不将所有可用的旅程分成更小的组(基于车辆类型,这就是 belongsToVehicleType() 函数的用途)。对于每组旅程,我必须 运行 std::sort 来实现该数据所需的顺序。

长话短说 - 在我的本地机器上一切正常(32 位 Ubuntu 16.04 和 Qt 5.4.2)但是当我尝试交叉编译它时(目标系统也使用 Qt 5.4.2,基于 Intel 的 32 位架构等)。请注意,如果我注释掉我调用 std::sort() 的所有行,即使使用交叉编译器,我的代码也会编译正常( 尽管链接器抱怨无法找到 libc.so.6某种原因)。您可以在下面看到 Qt Creator 输出的错误消息:

/opt/target-toolchain/crosstools/i686-unknown-linux-gnu/i686-unknown-linux-gnu/include/c++/4.7.4/bits/stl_algo.h:2277: error: passing 'const Data::Journey' as 'this' argument of 'bool Data::Journey::operator<(const Data::Journey&)' discards qualifiers [-fpermissive]

当我在任何 QList 上调用 std::sort 时,会出现此错误。例如:

QList<Journey> connectionsRE;

// Fill connectionsRE

if (connectionsRE.length())
{
    LOG(INFO) << "Regional train (Express) RE1-RE9 connections:";
    std::sort(connectionsRE.begin(), connectionsRE.end());  // <----------- ERROR STARTS FROM HERE!!!

    // Do something else with connectionsRE
}

在项目的build文件夹中运行ning make时的完整编译错误是

In file included from /opt/target-toolchain/crosstools/i686-unknown-linux-gnu/lib/gcc/i686-unknown-linux-gnu/4.7.4/../../../../i686-unknown-linux-gnu/include/c++/4.7.4/algorithm:63:0,
                 from /opt/target-toolchain/Qt/Qt5/include/QtCore/qglobal.h:81,
                 from /opt/target-toolchain/Qt/Qt5/include/QtCore/qnamespace.h:37,
                 from /opt/target-toolchain/Qt/Qt5/include/QtCore/qobjectdefs.h:41,
                 from /opt/target-toolchain/Qt/Qt5/include/QtCore/qobject.h:40,
                 from /opt/target-toolchain/Qt/Qt5/include/QtCore/QObject:1,
                 from /home/user/Projects/project/src/datafusionhandler.h:4,
                 from /home/user/Projects/project/src/datafusionhandler.cpp:1:
/opt/target-toolchain/crosstools/i686-unknown-linux-gnu/lib/gcc/i686-unknown-linux-gnu/4.7.4/../../../../i686-unknown-linux-gnu/include/c++/4.7.4/bits/stl_algo.h: In instantiation of '_RandomAccessIterator std::__unguarded_partition(_RandomAccessIterator, _RandomAccessIterator, const _Tp&) [with _RandomAccessIterator = QList<Data::Journey>::iterator; _Tp = Data::Journey]':
/opt/target-toolchain/crosstools/i686-unknown-linux-gnu/lib/gcc/i686-unknown-linux-gnu/4.7.4/../../../../i686-unknown-linux-gnu/include/c++/4.7.4/bits/stl_algo.h:2315:70:   required from '_RandomAccessIterator std::__unguarded_partition_pivot(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = QList<Data::Journey>::iterator]'
/opt/target-toolchain/crosstools/i686-unknown-linux-gnu/lib/gcc/i686-unknown-linux-gnu/4.7.4/../../../../i686-unknown-linux-gnu/include/c++/4.7.4/bits/stl_algo.h:2347:54:   required from 'void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size) [with _RandomAccessIterator = QList<Data::Journey>::iterator; _Size = int]'
/opt/target-toolchain/crosstools/i686-unknown-linux-gnu/lib/gcc/i686-unknown-linux-gnu/4.7.4/../../../../i686-unknown-linux-gnu/include/c++/4.7.4/bits/stl_algo.h:5483:4:   required from 'void std::sort(_RAIter, _RAIter) [with _RAIter = QList<Data::Journey>::iterator]'
/home/user/Projects/project/src/datafusionhandler.cpp:387:61:   required from here
/opt/target-toolchain/crosstools/i686-unknown-linux-gnu/lib/gcc/i686-unknown-linux-gnu/4.7.4/../../../../i686-unknown-linux-gnu/include/c++/4.7.4/bits/stl_algo.h:2277:4: error: passing 'const Data::Journey' as 'this' argument of 'bool Data::Journey::operator<(const Data::Journey&)' discards qualifiers [-fpermissive]
compilation terminated due to -Wfatal-errors.
src/CMakeFiles/project.dir/build.make:134: recipe for target 'src/CMakeFiles/project.dir/datafusionhandler.cpp.o' failed
make[2]: *** [src/CMakeFiles/project.dir/datafusionhandler.cpp.o] Error 1
CMakeFiles/Makefile2:1018: recipe for target 'src/CMakeFiles/project.dir/all' failed
make[1]: *** [src/CMakeFiles/project.dir/all] Error 2
Makefile:94: recipe for target 'all' failed
make: *** [all] Error 2

-Wfatal-errors 是为我的本地机器和目标系统构建的。也许我的 rootfs 目录(存储目标操作系统映像的地方)中的某些底层库太旧了?

我认为您可能需要将某些运算符函数声明为 const,例如:

bool operator > (const Journey &other) const
{
  ...
}