簡體   English   中英

在std :: sort中崩潰 - 排序沒有嚴格的弱排序

[英]Crash in std::sort - sorting without strict weak ordering

我正在嘗試對項目的矢量進行排序。 如代碼注釋中所述,順序應為:

具有更多動作點( mAp )的參與者先行。 當存在平局時,具有與戰斗的發起者相同的處置( mDisposition )的參與者( mBattleInitiator )首先出現。

以下代碼(簡化示例)在macOS上崩潰,可能是由於我的排序實現不正確:

#include <QtCore>

class AiComponent
{
public:
    enum Disposition {
        Friendly,
        Hostile
    };

    AiComponent(Disposition disposition) : mDisposition(disposition) {}
    ~AiComponent() { qDebug() << "Destroying AiComponent"; }

    Disposition mDisposition;
};

class BattleManager
{
public:
    BattleManager() : mBattleInitiator(AiComponent::Hostile) {}

    class Turn {
    public:
        Turn() : mAp(1) {}

        Turn(QSharedPointer<AiComponent> aiComponent) :
            mAiComponent(aiComponent),
            mAp(1)
        {
        }

        Turn(const Turn &rhs) :
            mAiComponent(rhs.mAiComponent),
            mAp(1)
        {
        }

        QSharedPointer<AiComponent> mAiComponent;
        int mAp;
    };

    void addToTurnQueue(QSet<QSharedPointer<AiComponent>> aiComponents);

    AiComponent::Disposition mBattleInitiator;
    QVector<Turn> mTurnQueue;
    Turn mActive;
};

void BattleManager::addToTurnQueue(QSet<QSharedPointer<AiComponent> > aiComponents)
{
    foreach (auto aiComponent, aiComponents) {
        mTurnQueue.append(Turn(aiComponent));
    }

    // Sort the participants so that ones with more action points (mAp) go first.
    // When there is a tie, participants with the same disposition (mDisposition)
    // as the initiator of the battle (mBattleInitiator) go first.
    std::sort(mTurnQueue.begin(), mTurnQueue.end(), [=](const Turn &a, const Turn &b) {
        if (a.mAp > b.mAp)
            return true;

        if (a.mAp < b.mAp)
            return false;

        // At this point, a.mAp is equal to b.mAp, so we must resolve the tie
        // based on mDisposition.
        if (a.mAiComponent->mDisposition == mBattleInitiator)
            return true;

        if (b.mAiComponent->mDisposition == mBattleInitiator)
            return false;

        return false;
    });
}

int main(int /*argc*/, char */*argv*/[])
{
    BattleManager battleManager;

    for (int i = 0; i < 20; ++i) {
        qDebug() << "iteration" << i;

        QSet<QSharedPointer<AiComponent>> participants;

        AiComponent::Disposition disposition = i % 2 == 0 ? AiComponent::Hostile : AiComponent::Friendly;
        QSharedPointer<AiComponent> ai(new AiComponent(disposition));
        participants.insert(ai);

        battleManager.addToTurnQueue(participants);
    }

    // This should print (1 1), (1 1), ... (1 0), (1 0)
    foreach (auto turn, battleManager.mTurnQueue) {
        qDebug() << "(" << turn.mAp << turn.mAiComponent->mDisposition << ")";
    }

    return 0;
}

我已經查看了有關該主題的其他答案。 他們中的大多數只是說“將它實現為> b”,這在我的情況下是行不通的。 有一些似乎相關,但沒有幫助我:

實現我追求目標的最簡單方法是什么?

墜機原因尚未解釋。 std :: sort的大多數實現都基於快速排序,特別是Hoare分區方案,只要元素值<pivot values,就會從左向右掃描數組,並且只要元素值從右向左掃描數組>樞軸值。 這些掃描指的是找到元素值=透視值將停止掃描的事實,因此不會檢查超出數組邊界的掃描。 如果用戶提供的less比較函數在相等元素的情況下返回true,則任一掃描都可能超出數組邊界並導致崩潰。

在調試構建的情況下,可以進行用戶比較功能的測試以確保比較小於且不小於或等於,但是對於發布構建,目標是速度,因此不執行這些檢查。

我將在您的代碼中刪除注釋並解釋它的錯誤(如果有的話),以及如何修復它。

// Sort the participants so that ones with more action points (mAp) go first.

目前很好

// When there is a tie, participants with the same disposition (mDisposition) as the initiator of the battle (mBattleInitiator) go first.

如果兩個參與者都具有與發起人相同的處置方式會怎樣? 即使您可以保證沒有2個元素滿足這個條件,也允許排序算法將元素與自身進行比較。 在這種情況下,該測試將返回true ,違反嚴格弱排序的條件之一,即元素必須與其自身相等(即compare(a,a)必須始終為false)。

也許您想要說如果a與發起者具有相同的處置方式,而b則沒有,那么a應該被認為小於b 這可以編碼為:

return dispositionOfA == mBattleInitiator && dispsitionOfB != mBattleInitiator;

所以你的完整測試看起來像這樣:

if (a.mAp > b.mAp)
    return true;
if (a.mAp < b.mAp)
    return false;

return a.mAiComponent->mDisposition == mBattleInitiator &&
       b.mAiComponent->mDisposition != mBattleInitiator;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM