使用Lambda和可变参数模板延迟初始化对象-任意传递值

时间:2019-06-10 10:35:44

标签: c++

我正在尝试在C ++ 17中创建一个“初始化程序”函数,该函数可对对象的构造函数进行调用,以便在知道其余可变参数时将其推迟到以后的时间。

我已遵循c++ lambdas how to capture variadic parameter pack from the upper scope处的示例,该示例可以编译,但不处理通过引用传递的值。

完整的示例可以在https://repl.it/repls/IroncladToughExabyte

中找到

我也将代码复制到此处,以便将其捕获以供将来参考:

#include <iostream>
#include <functional>
#include <memory>
#include <utility>
#include <limits>

using std::cout;
using std::endl;
using std::function;
using std::move;
using std::unique_ptr;
using std::make_unique;

typedef uint8_t MyParam;

struct SomethingBig {
    int a;
    int b;
};

class GamePlayingAlgorithm {
 public:
    const MyParam &a_;
    const MyParam b_;

    GamePlayingAlgorithm(const MyParam &a, MyParam b)
        : a_(a), b_(b) {};
    virtual void play() = 0;
};

class PassByRef: public GamePlayingAlgorithm {
 public:
    SomethingBig &stuff_;
    inline explicit PassByRef(const MyParam &a, MyParam b, SomethingBig &stuff)
        : GamePlayingAlgorithm(a, b), stuff_(stuff) {}
    void play() override {
        cout << stuff_.a << endl;
    }
};

typedef function<unique_ptr<GamePlayingAlgorithm>(const MyParam &, MyParam)>
    PreparedAlgorithm;

template<typename...>
struct pack {};

template<typename T, typename Tup, typename... TArgs, std::size_t... Is>
std::unique_ptr<T>
helper(const MyParam &domain, MyParam pl, Tup &&tup, pack<TArgs...>, std::index_sequence<Is...>) {
    return std::make_unique<T>(domain, pl, static_cast<TArgs>(std::get<Is>(tup))...);
}

// use tuple packing/unpacking
template<typename T, typename... Args>
PreparedAlgorithm createInitializer1(Args &&... args) {
    return [tup = std::make_tuple(std::forward<Args>(args)...)](const MyParam &domain,
                                                                MyParam pl) mutable {
        return helper<T>(domain,
                         pl,
                         std::move(tup),
                         pack<Args &&...>{},
                         std::index_sequence_for<Args...>{});
    };
}

// use simple forwarding with reference in lambda
template<typename T, typename... Args>
PreparedAlgorithm createInitializer2(Args &&... args) {
    return [&](const MyParam &domain, MyParam pl) -> unique_ptr<GamePlayingAlgorithm> {
        return make_unique<T>(domain, pl, std::forward<Args>(args) ...);
    };
}

int main() {
    SomethingBig stuffRef1 = {100, 200};
    PreparedAlgorithm preparedRef1 = createInitializer1<PassByRef>(stuffRef1);
    auto algRef1 = preparedRef1(1, 1);
    cout << "algRef1: ";
    algRef1->play();
    stuffRef1.a = 500;
    cout << "algRef1 (after update): ";
    algRef1->play();


    SomethingBig stuffRef2 = {100, 200};
    PreparedAlgorithm preparedRef2 = createInitializer2<PassByRef>(stuffRef2);
    auto algRef2 = preparedRef2(1, 1);
    cout << "algRef2: ";
    algRef2->play();
    stuffRef2.a = 500;
    cout << "algRef2 (after update): ";
    algRef2->play();
}

运行此命令的输出是:

algRef1: 100
algRef1 (after update): 100
algRef2: 100
algRef2 (after update): 500

问题是algRef1未更新。

algRef2已更新, ,但是它使用的是未定义的操作 ,实际上,它确实会破坏我的较大源代码。

问题是-如何更改createInitializer1createInitializer2的实现,以便正确定义它们?

谢谢!

1 个答案:

答案 0 :(得分:0)

当我将丢失的虚拟析构函数添加到GamePlayingAlgorithm时,未定义的行为开始引起更多麻烦。

要调试此类问题,请用指针替换所有引用,它们不会被意外转换为临时副本,而是会导致简单的编译错误。真正的返回类型可以通过使该函数称为templated并故意在其中引起错误来进行检查,以便编译器将打印具有推断类型的回溯。

在您的情况下,std::get<Is>(tup)没有返回引用,而static_cast没有解决该引用。

如果我将其编辑为使用指针,它将正常工作:

template<typename T, typename Tup, typename... TArgs, std::size_t... Is>
std::unique_ptr<T>
helper(const MyParam &domain, MyParam pl, Tup &tup, pack<TArgs...>, std::index_sequence<Is...>) {
    return std::make_unique<T>(domain, pl, *std::get<Is>(tup)...);
}

// use tuple packing/unpacking
template<typename T, typename... Args>
PreparedAlgorithm createInitializer1(Args &... args) {
    return [tup = std::make_tuple<Args*...>(&args...)](const MyParam &domain,
                                                                MyParam pl) mutable {
        return helper<T>(domain,
                         pl,
                         tup,
                         pack<Args &&...>{},
                         std::index_sequence_for<Args...>{});
    };
}

现在,这是您的完整代码:https://repl.it/repls/TenseElectricMolecule