使用可变参数模板绑定 make_shared
bind make_shared with variadic template
我正在尝试编写以下工厂 class,但找不到正确的语法:
template<class T, typename... TArgs>
class Factory {
public:
Factory(TArgs... args) {
creator_ = std::bind(&std::make_shared<T, TArgs...>, args...);
// ^^^ some error around here
}
std::shared_ptr<T> Create() const {
return creator_();
}
private:
std::function<std::shared_ptr<T>()> creator_;
};
我是这样使用工厂的:
class Foo {
public:
Foo(bool value) {}
};
class Bar {
public:
Bar(const std::string& value) {}
};
Factory<Foo, bool> f1(true);
Factory<Bar, std::string> f2("string");
这些是我在声明 f1
和 f2
时遇到的错误:
error: no match for 'operator=' (operand types are 'std::function<std::shared_ptr<Foo>()>' and 'std::_Bind_helper<false, std::shared_ptr<Foo> (*)(bool&&), bool&>::type {aka std::_Bind<std::shared_ptr<Foo> (*(bool))(bool&&)>}')
creator_ = std::bind(&std::make_shared<T, TArgs...>, args...);
^
error: no match for 'operator=' (operand types are 'std::function<std::shared_ptr<Bar>()>' and 'std::_Bind_helper<false, std::shared_ptr<Bar> (*)(std::basic_string<char>&&), std::basic_string<char, std::char_traits<char>, std::allocator<char> >&>::type {aka std::_Bind<std::shared_ptr<Bar> (*(std::basic_string<char>))(std::basic_string<char>&&)>}')
creator_ = std::bind(&std::make_shared<T, TArgs...>, args...);
^
我必须使用 std::bind
的正确语法是什么?
std::make_shared
声明如下:
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
因此,std::make_shared<T, TArgs...>
将导致采用右值引用的函数,该引用不会绑定到 args...
。一个简单的解决方法是通过折叠引用强制它采用左值引用:
creator_ = std::bind(&std::make_shared<T,TArgs&...>, args...);
// ^
另一种方法是改用 lambda,这样可读性更好:
creator_ = [=](){return std::make_shared<T>(args...);};
因此,不使用 bind 的最高效 C++14 解决方案实际上很笨拙。
template<class T>
struct Factory {
template<class...Args>
Factory(Args&&... args):
creator_(
make_creator(
std::index_sequence_for<Args...>{},
std::make_tuple( std::forward<Args>(args)...
)
)
{}
std::shared_ptr<T> operator()() const {
return creator_();
}
private:
using signature = std::shared_ptr<T>();
using creator = std::function<signature>;
creator creator_;
// helper, to make a lambda with a tuple to unpack:
template<class Tup, size_t...Is>
static creator make_creator(std::index_sequence<Is...>, Tup&& tup) {
return [tup = std::forward<Tup>(tup)]{
return std::make_shared<T>( std::get<Is>(tup)... );
};
}
};
这个版本有一些改进。
首先,无需指定您创建 T
的参数:
Factory<Foo> f1(true);
Factory<Bar> f2("string");
其次,我们有 f1()
而不是 f1.Create()
。调用工厂显然会创建工厂创建的东西——调用命名方法只是噪音。
我们可以更进一步:
template<class T>
using Factory = std::function<std::shared_ptr<T>()>;
namespace details {
template<class T, class Tup, size_t...Is>
Factory<T> make_factory(std::index_sequence<Is...>, Tup&& tup) {
return [tup = std::forward<Tup>(tup)]{
return std::make_shared<T>( std::get<Is>(tup)... );
};
}
}
template<class T, class...Args>
Factory<T> make_factory(Args&&...args) {
return details::make_factory<T>(
std::index_sequence_for<Args...>{},
std::make_tuple( std::forward<Args>(args)... )
);
}
我们完全取消了 Factory
类型——Factory<T>
只是成为 std::function
的别名,它不带任何东西,而 returns 是 shared_ptr<T>
.
现在我觉得details::make_factory
很无聊。
namespace details {
template<class F, class Tup, size_t...Is>
auto invoke( F&& f, Tup&& tup, std::index_sequence<Is...> )
-> std::result_of_t<F( std::tuple_element_t<Is, std::decay_t<Tup>>... )>
{
return std::forward<F>(f)( std::get<Is>(std::forward<Tup>(tup))... );
}
}
template<class F, class Tup, size_t...Is>
auto invoke( F&& f, Tup&& tup )
{
using count = std::tuple_size< std::decay_t<Tup> >;
using indexes = std::make_index_sequence< count{} >;
return details::invoke(
std::forward<F>(f),
std::forward<Tup>(tup),
indexes{}
);
}
template<class T>
auto shared_maker() {
return [](auto&&...args){
return std::make_shared<T>( decltype(args)(args)... );
};
}
template<class T, class...Args>
Factory<T> make_factory(Args&&...args) {
return [tup=std::make_tuple(std::forward<Args>(args)...)]{
return invoke(
shared_maker<T>(),
tup
);
};
}
live example,这里我们把'invoke a function from a tuple'单独写成invoke
。
template<class T>
const auto shared_maker = [](auto&&...args){
return std::make_shared<T>(decltype(args)(args)...);
};
会稍微圆滑,但 gcc 5.2.0 doesn't like it.
我正在尝试编写以下工厂 class,但找不到正确的语法:
template<class T, typename... TArgs>
class Factory {
public:
Factory(TArgs... args) {
creator_ = std::bind(&std::make_shared<T, TArgs...>, args...);
// ^^^ some error around here
}
std::shared_ptr<T> Create() const {
return creator_();
}
private:
std::function<std::shared_ptr<T>()> creator_;
};
我是这样使用工厂的:
class Foo {
public:
Foo(bool value) {}
};
class Bar {
public:
Bar(const std::string& value) {}
};
Factory<Foo, bool> f1(true);
Factory<Bar, std::string> f2("string");
这些是我在声明 f1
和 f2
时遇到的错误:
error: no match for 'operator=' (operand types are 'std::function<std::shared_ptr<Foo>()>' and 'std::_Bind_helper<false, std::shared_ptr<Foo> (*)(bool&&), bool&>::type {aka std::_Bind<std::shared_ptr<Foo> (*(bool))(bool&&)>}')
creator_ = std::bind(&std::make_shared<T, TArgs...>, args...);
^
error: no match for 'operator=' (operand types are 'std::function<std::shared_ptr<Bar>()>' and 'std::_Bind_helper<false, std::shared_ptr<Bar> (*)(std::basic_string<char>&&), std::basic_string<char, std::char_traits<char>, std::allocator<char> >&>::type {aka std::_Bind<std::shared_ptr<Bar> (*(std::basic_string<char>))(std::basic_string<char>&&)>}')
creator_ = std::bind(&std::make_shared<T, TArgs...>, args...);
^
我必须使用 std::bind
的正确语法是什么?
std::make_shared
声明如下:
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
因此,std::make_shared<T, TArgs...>
将导致采用右值引用的函数,该引用不会绑定到 args...
。一个简单的解决方法是通过折叠引用强制它采用左值引用:
creator_ = std::bind(&std::make_shared<T,TArgs&...>, args...);
// ^
另一种方法是改用 lambda,这样可读性更好:
creator_ = [=](){return std::make_shared<T>(args...);};
因此,不使用 bind 的最高效 C++14 解决方案实际上很笨拙。
template<class T>
struct Factory {
template<class...Args>
Factory(Args&&... args):
creator_(
make_creator(
std::index_sequence_for<Args...>{},
std::make_tuple( std::forward<Args>(args)...
)
)
{}
std::shared_ptr<T> operator()() const {
return creator_();
}
private:
using signature = std::shared_ptr<T>();
using creator = std::function<signature>;
creator creator_;
// helper, to make a lambda with a tuple to unpack:
template<class Tup, size_t...Is>
static creator make_creator(std::index_sequence<Is...>, Tup&& tup) {
return [tup = std::forward<Tup>(tup)]{
return std::make_shared<T>( std::get<Is>(tup)... );
};
}
};
这个版本有一些改进。
首先,无需指定您创建 T
的参数:
Factory<Foo> f1(true);
Factory<Bar> f2("string");
其次,我们有 f1()
而不是 f1.Create()
。调用工厂显然会创建工厂创建的东西——调用命名方法只是噪音。
我们可以更进一步:
template<class T>
using Factory = std::function<std::shared_ptr<T>()>;
namespace details {
template<class T, class Tup, size_t...Is>
Factory<T> make_factory(std::index_sequence<Is...>, Tup&& tup) {
return [tup = std::forward<Tup>(tup)]{
return std::make_shared<T>( std::get<Is>(tup)... );
};
}
}
template<class T, class...Args>
Factory<T> make_factory(Args&&...args) {
return details::make_factory<T>(
std::index_sequence_for<Args...>{},
std::make_tuple( std::forward<Args>(args)... )
);
}
我们完全取消了 Factory
类型——Factory<T>
只是成为 std::function
的别名,它不带任何东西,而 returns 是 shared_ptr<T>
.
现在我觉得details::make_factory
很无聊。
namespace details {
template<class F, class Tup, size_t...Is>
auto invoke( F&& f, Tup&& tup, std::index_sequence<Is...> )
-> std::result_of_t<F( std::tuple_element_t<Is, std::decay_t<Tup>>... )>
{
return std::forward<F>(f)( std::get<Is>(std::forward<Tup>(tup))... );
}
}
template<class F, class Tup, size_t...Is>
auto invoke( F&& f, Tup&& tup )
{
using count = std::tuple_size< std::decay_t<Tup> >;
using indexes = std::make_index_sequence< count{} >;
return details::invoke(
std::forward<F>(f),
std::forward<Tup>(tup),
indexes{}
);
}
template<class T>
auto shared_maker() {
return [](auto&&...args){
return std::make_shared<T>( decltype(args)(args)... );
};
}
template<class T, class...Args>
Factory<T> make_factory(Args&&...args) {
return [tup=std::make_tuple(std::forward<Args>(args)...)]{
return invoke(
shared_maker<T>(),
tup
);
};
}
live example,这里我们把'invoke a function from a tuple'单独写成invoke
。
template<class T>
const auto shared_maker = [](auto&&...args){
return std::make_shared<T>(decltype(args)(args)...);
};
会稍微圆滑,但 gcc 5.2.0 doesn't like it.