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

Deferred initialization of an object using lambdas and variadic templates - pass values arbitrarily

本文关键字:对象 初始化 任意传 延迟 lambda 变参 参数 使用      更新时间:2023-10-16

我正在尝试在 C++17 中创建一个"初始值设定项"函数,该函数对对象的构造函数进行调用,以便它可以推迟到以后的时间,当其余可变参数已知时。

我遵循了 c++ lambda 中的示例,如何从编译但不处理引用传递值的上层范围捕获可变参数包。

完整的运行示例可在 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的实现以正确定义它们?

谢谢!

当我将缺少的虚拟析构函数添加到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