Ошибка отладочного утверждения при использовании std :: vector в статической библиотечной функции, вызываемой из глобального объекта - PullRequest
1 голос
/ 18 июля 2011

Сейчас я нахожусь в процессе очистки кода моего решения, которое состоит из статической библиотеки и двух приложений, которые зависят от нее.Как часть этой очистки кода, я конвертировал все свои циклы через std :: vectors, чтобы использовать итераторы, а не индексы.Все шло хорошо, пока я не преобразовал функцию (в библиотеке), которая вызывается при создании глобального объекта (в приложении).Рассматриваемая функция заполняет std :: vector, а затем ищет вектор для объекта, который соответствует описанию, переданному функции, возвращая первое совпадение.Если совпадений не найдено, передняя часть вектора возвращается.

Мне удалось уменьшить проблему до следующего кода:

Библиотека - Bar.h

struct Bar
{
    int val;

    Bar(int val = 0);

    static Bar const& ByVal(int val);
};

Библиотека - Bar.cpp

#include "Bar.h"
#include <vector>

using namespace std;

namespace { vector<Bar> bars; } // It is irrelevant whether bars is in an
                                // anonymous namespace or not; the results are
                                // the same.

Bar::Bar(int _val) : val(_val) { }

Bar const& Bar::ByVal(int val)
{
    if (bars.empty())
    {
        bars.push_back(Bar(1));
        bars.push_back(Bar(2));
    }

#if 1
    for (vector<Bar>::const_iterator it = bars.begin();
         it != bars.end();
         ++it) // The assertion fails here. However, when the for loop is
               // replaced with a while loop, it's the it != bars.end() part
               // that fails.
    {
        if (it->val == val)
            return *it;
    }

    return bars.front();
#else
    for (size_t i = 0;
         i < bars.size();
         ++i)
    {
        if (bars[i].val == val)
            return bars[i];
    }

    return bars[0];
#endif
}

Приложение - Foo.cpp

#include <Bar.h>
#include <iostream>

using namespace std;

struct Foo
{
    Foo()
    {
        Bar bar = Bar::ByVal(0);
        cout << bar.val << endl;
    }
};

Foo foo;

int main(int argc, char** argv)
{
    return 0;
}

Если условное выражение препроцессора в Bar.cpp изменено на 0, код выполняется безупречно.В противном случае отображается следующее утверждение:

Debug Assertion Failed!

Program: C:\Work\Reduction\Debug\Foo.exe
File: c:\program files (x86)\microsoft visual studio 10.0\vc\include\vector
Line: 238

Expression: vector iterators not compatible

Это новое решение с новыми проектами в Visual Studio 2010. Единственные параметры, которые были изменены в проектах, были те, которые были необходимы для получения приложения.связать со статической библиотекой.

Пытаясь выяснить, что является причиной сбоя, я обнаружил, что код работает при следующих условиях:

  • При компиляции в Releasemode.
  • Когда вектор баров библиотеки объявляется как extern и определяется в самом приложении.
  • Когда переменная foo приложения перемещается внутри функции main ().
  • Когда код из библиотеки целиком переносится в приложение.
  • При компиляции в Visual Studio 2008.

Любая помощь будет принята с благодарностью, даже если это будет означатьвернуться к использованию индексов или VS2008.Я отчаянно искал и бился головой по этому вопросу уже почти два дня.

1 Ответ

5 голосов
/ 18 июля 2011

Стандарт C ++ не гарантирует, что конструктор bars будет вызываться раньше, чем конструктор foo. Это иногда называют 'статическим порядком инициализации фиаско' ; вам может повезти, например, в VS2008, но это не значит, что проблема исчезнет. Ссылка на странице предлагает несколько потенциальных решений этой проблемы, одним из которых является использование статического уровня функций, чтобы обеспечить его инициализацию перед использованием.

...