我正在尝试在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
已更新, ,但是它使用的是未定义的操作 ,实际上,它确实会破坏我的较大源代码。
问题是-如何更改createInitializer1
或createInitializer2
的实现,以便正确定义它们?
谢谢!
答案 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