std::vector back() 的奇怪行为

Odd behavior of std::vector back()

以下代码在指定位置断言 "iterator+offset is out of range."

void Network::PushInput(int c, int h, int w) {
    Input* input = new Input(batch, c, h, w, data);
    layers.push_back(input);    // this happens to be the first push_back()
//  layers.push_back(input);    // doing another doesn't change the assert!
    Layer *foo = layers.back();  // asserts here
    Layer *baz = layers[layers.size()-1];  // does not assert
}

输入是图层的 public 子class。图层声明为

std::vector<Layer *>layers;

如果我尝试用更多普通模板类型(例如 int*)复制上面的内容,back() 将按预期工作而没有断言。不知何故,模板类型在这里很重要。 (注意:_ITERATOR_DEBUG_LEVEL 为 2,这会触发断言检查向量 class。)

我不想直接将代码中的所有 back() 更改为 size()-1,而是想了解这里发生了什么。

有什么想法吗? (我会继续扰乱代码,直到找到明显的原因,但希望这对其他人来说是显而易见的。)

(我使用的是 Visual Studio 2013 社区版,如果这很重要的话。)

.....

这是一个显示问题的独立编译文件:

#include <vector>

using namespace std;

namespace layer {
    class Layer {
    public:
        Layer(float alpha = 0, float momentum = 0.9f, float weight_decay = 0);
        virtual ~Layer();

        // three virtual method that all layers should have
        virtual void forward(bool train = true) = 0;
        virtual void backward() = 0;
        virtual void update() = 0;

        void adjust_learning(float scale); // change the learning rate

        Layer* prev;                    // previous layer
        Layer* next;                    // next layer
        float* data;                    // X': output (cuDNN y)
        int batch;                      // n: batch size
        float alpha;                    // learning rate
        float momentum;                 // beta: momentum of gradient
        float weight_decay;             // gamma: weight decay rate
    };
} /* namespace layer */

namespace layer {
    Layer::Layer(float alpha_, float momentum_, float weight_decay_)
    {
        std::memset(this, 0, sizeof(*this));
        alpha = alpha_;
        momentum = momentum_;
        weight_decay = weight_decay_;
    }

    Layer::~Layer() {}

    void Layer::adjust_learning(float scale) {
        alpha *= scale;
    }
}

namespace layer {

    class Input : public Layer {
    public:
        Input(int n, int c, int h, int w, float* _data);
        virtual ~Input();
        void forward(bool train = true);
        void backward();
        void update();
    };

}

namespace layer {

    Input::Input(int n, int c, int h, int w, float* _data) : Layer() {
        prev = NULL;

        batch = n;
        data = _data;
    }

    Input::~Input() {
        data = NULL;
    }

    void Input::forward(bool train) {
        // nothing
    }

    void Input::backward() {
        // nothing
    }

    void Input::update() {
        // nothing
    }

}

using namespace layer;

namespace model {

    class Network {
    private:
        std::vector<Layer*> layers; // list of layers
        bool has_input, has_output; // sanity check
        float* data; // input on device
        int batch; // whole size of data, batch size
    public:
        Network(int batch_size);
        virtual ~Network();
        void PushInput(int c, int h, int w);
    };
}

namespace model {
    void Network::PushInput(int c, int h, int w) {

        Input* input = new Input(batch, c, h, w, data);
        layers.push_back(input);
        Layer *foo = layers.back();  // **WHY DOES THIS ASSERT??**
    }
    Network::Network(int _batch) {
        std::memset(this, 0, sizeof(*this));
        batch = _batch;
    }

    Network::~Network() {
        for (Layer* l : layers)
            delete l;
    }
}

void main()
{
    model::Network foo(10);

    foo.PushInput(2, 3, 4);
}

您的代码中有未定义的行为

Layer构造函数中你做

std::memset(this, 0, sizeof(*this));

问题是上面的调用也会清除虚函数table(它是对象的一部分)。之后调用的任何虚函数都不会按预期工作,如果有的话。这包括对象的销毁,因为析构函数是虚拟的。