I have a builder
class that implements a template <typename M, typename ... Args> register_builder
method.
This method takes a string and a bunch of arguments and passes it to a const auto& factory = [&]() { return std::make_unique<M>(name, std::forward<Args>(args)...);};
that is used to generate some objects.
Depending on the optimization flag I pass to g++ or clang (14 and 15) this function builds the correct object or not.
With (-std=c++17), the output of the code snippet is correct: A 0 -3.14
while with (-std=c++17 -O3) the output is wrong: A 1000 2.79
It looks to me like only the last given variadic arguments are taken into account, but I can't see why.
#include <string>
#include <functional>
#include <memory>
#include <map>
#include <iostream>
struct iObj{
using ptr_t = std::unique_ptr<iObj>;
iObj(const std::string& name, const int& id, const double& x):_name(name), _id(id), _x(x) {};
virtual ~iObj(){};
virtual void run() = 0;
std::string _name;
int _id;
double _x;
};
struct myObj : iObj {
myObj(const std::string& name, const int& id, const double& x) : iObj(name, id, x) {};
void run() override {
std::cout << _name << " " <<_id << " " << _x;
};
};
struct builder
{
using factory_t = std::function<iObj::ptr_t()>;
template <typename M, typename ... Args>
void register_builder(const std::string &name, Args &&...args)
{
if (_factories.find(name) != _factories.end())
return;
const auto& factory = [&]() {
return std::make_unique<M>(name, std::forward<Args>(args)...);
};
_factories[name] = std::move(factory);
}
iObj::ptr_t build(const std::string& name) const
{
const auto& it = _factories.find(name);
return it->second();
}
std::map<std::string, factory_t> _factories;
};
int main()
{
builder b;
b.register_builder<myObj>("A", 0, -3.14);
b.register_builder<myObj>("B", 1000, 2.79);
auto obj = b.build("A");
obj->run();
return 0;
}
This is reproducible on Linux and on godbolt.org on gcc12.2.