为什么 class 模板中的成员函数在同一地址创建相同的对象

Why a member function in a class template creates the same object in the same address

我正在测试我最近写的一个无锁工作窃取队列。但是,我发现了一个奇怪的问题,当我将一个新项目推送到队列时,它只会 returns 我最后推送的同一个对象。我花了一天时间,但我仍然不明白为什么会这样。 这是输出,每次,数据都是相同的被推送到队列,这就是为什么我只有最后一个结果是 45,它应该是 45 个 res 项目。任何人都可以在这里帮助我:(

下面是简化代码:

#include <memory>
#include <functional>
#include <type_traits>

class FunctionWrapper {
private:
    class ImplInterface {
    public:
        virtual void invoke() = 0;
        virtual ~ImplInterface() {}
    };

    std::unique_ptr<ImplInterface> impl;

    template <typename F, typename... Args>
    class Impl : public ImplInterface {
    private:
        std::function<void()> callBack;
    public:
        Impl(F&& f_, Args&&... args_) {
            callBack = [&f_, &args_...]() { f_(std::forward<Args>(args_)...); };
        }

        void invoke() override { callBack(); } 
    };

public: 
    template <typename F, typename... Args>
    FunctionWrapper(F&& f_, Args&&... args_) : impl(new Impl<F, Args...>(std::forward<F>(f_), std::forward<Args>(args_)...)) {}

    void operator()() { impl->invoke(); }

    FunctionWrapper() = default;
    FunctionWrapper(FunctionWrapper&& other) : impl(std::move(other.impl)) {}
    FunctionWrapper& operator=(FunctionWrapper&& other) {
        impl = std::move(other.impl);
        return *this;
    }

    FunctionWrapper(const FunctionWrapper&) = delete;
    FunctionWrapper(FunctionWrapper&) = delete;
    FunctionWrapper& operator=(const FunctionWrapper&) = delete;
};
#include <atomic>
#include <array>

#include <iostream>

#include "functionwrapper.h"

class LockFreeWorkStealingQueue {
private:
    using DataType = FunctionWrapper;
    static constexpr auto DEFAULT_COUNT = 2048u;
    static constexpr auto MASK = DEFAULT_COUNT - 1u;
    std::array<DataType, DEFAULT_COUNT> q;
    unsigned int ft{0};
    unsigned int bk{0};
public:
    LockFreeWorkStealingQueue() {}
    LockFreeWorkStealingQueue(const LockFreeWorkStealingQueue&) = delete;
    LockFreeWorkStealingQueue& operator=(const LockFreeWorkStealingQueue&) = delete;

    void push_back(DataType data) {
        std::cout << "bk: " << (bk&MASK) << " &data: " << &data << std::endl;
        q[bk & MASK] = std::move(data);
        bk++;
    }

    bool try_pop_back(DataType& res) {
        if (bk > ft) {
            res = std::move(q[(bk - 1) & MASK]);
            bk--;
            return true;
        }
        return false;
    }

};
#include "lockfreeworkstealingqueue.h"
#include <iostream>
#include <algorithm>
#include <numeric>
#include <cassert>
#include <vector>

constexpr unsigned int NUM = 4;

void sumOver(const std::vector<int>& v, int& res) {
    res = std::accumulate(v.begin(), v.end(), 0);
    //std::cout << "call sumOver, res = " << res << std::endl;
    //std::cout << "call sumOver, addr: "  << &res << std::endl;
}

int main () {
    std::vector<int> v { 1,2,3,4,5,6,7,8,9 };
    std::vector<int> res(NUM, -1);
    std::vector<LockFreeWorkStealingQueue> wsq(4);

    {

        for (auto i = 0; i < 4; ++i) {
            for (auto j = 0; j < NUM / 4; ++j) {
                std::cout << "push_back addr: " << &res[i*(NUM/4)+j] << std::endl;
                wsq[i].push_back(FunctionWrapper(sumOver, std::ref(v), std::ref(res.at(i*(NUM/4)+j)))); 
            }
        }

        FunctionWrapper f;
        for (auto i = 0; i < 4; ++i) {
            for (auto j = 0; j < NUM / 4; ++j) {
                if(wsq[i].try_pop_back(f)) {
                    f();
                }
            }
        }
    } 

    for (auto i = 0; i < 4; ++i) {
        for (auto j = 0; j < NUM / 4; ++j) {
            std::cout << "res[" << i*(NUM/4)+j << "]=" << res[i*(NUM/4)+j] << std::endl;
        }
    }

    return 0;
}

编辑:我对 functionwrapper.h 进行了更改以反映评论。现在效果很好。

#include <memory>
#include <functional>

class FunctionWrapper {
private:
    std::function<void()> callback;
public: 
    template <typename F, typename... Args>
    FunctionWrapper(F&& f_, Args&&... args_) : callback([f_, args_...]() { f_(args_...); }) {}

    void operator()() { callback(); }

    FunctionWrapper() = default;
    FunctionWrapper(FunctionWrapper&& other) : callback(std::move(other.callback)) {}
    FunctionWrapper& operator=(FunctionWrapper&& other) {
        callback = std::move(other.callback);
        return *this;
    }

    FunctionWrapper(const FunctionWrapper&) = delete;
    FunctionWrapper(FunctionWrapper&) = delete;
    FunctionWrapper& operator=(const FunctionWrapper&) = delete;
};

FunctionWrapper::Impl 中的 lambda 捕获对临时 std::reference_wrapper 实例的引用(由 main 中的 std::ref 调用产生)。到实际调用 lambda 时,那些临时对象早已被销毁并且引用悬空。因此,您的程序通过访问生命周期已结束的对象而表现出未定义的行为。

您想改为按值捕获,如

Impl(F&& f_, Args&&... args_) {
  callBack = [f_, args_...]() { f_(args_...); };
}

Demo