所以在c++中现在有make_from_tuple as:
T obj = std::make_from_tuple<T>( { Args... args } ); // args represents a tuple
但是怎么做呢?
T* obj = std::make_new_from_tuple<T*>( { Args... args } );
有make_shared和make_unique,但这两个都不需要一个元组(我不确定如何从元组中提取参数,如果这是要去的方向,因为你总是可以make_unique然后释放,如果你想要原始指针)。
非常简单的例子1:
struct A
{
int i_; double d_; std::string s_;
A( int i, double d, const std::string& s ) : i_(i), d_(d), s_(s) {}
};
auto aTuple = std::make_tuple( 1, 1.5, std::string("Hello") );
对于一个更复杂的例子,如果元组包含一个想要转发的unique_ptr,我也希望它能工作。
你基本上可以从cppreference的Possible实现中复制实现,插入一个make_unique
,它就可以工作了:
#include <string>
#include <memory>
namespace detail {
template<class T, class Tuple, std::size_t... I>
constexpr std::unique_ptr<T> make_new_from_tuple_impl(Tuple&& t, std::index_sequence<I...>)
{
static_assert(std::is_constructible_v<T,
decltype(std::get<I>(std::declval<Tuple>()))...>);
#if __cpp_lib_reference_from_temporary >= 202202L
if constexpr (std::tuple_size_v<std::remove_reference_t<Tuple>> == 1) {
using tuple_first_t = decltype(std::get<0>(std::declval<Tuple>()));
static_assert(!std::reference_constructs_from_temporary_v<T, tuple_first_t>);
}
#endif
return std::make_unique<T>(std::get<I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template<class T, class Tuple>
constexpr std::unique_ptr<T> make_new_from_tuple(Tuple&& t)
{
return detail::make_new_from_tuple_impl<T>(std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}
struct A
{
int i_; double d_; std::string s_;
A( int i, double d, const std::string& s ) : i_(i), d_(d), s_(s) {}
};
int main()
{
auto aTuple = std::make_tuple( 1, 1.5, std::string("Hello") );
auto a = make_new_from_tuple<A>(std::move(aTuple));
}
或使用原始指针:
#include <string>
#include <memory>
namespace detail {
template<class T, class Tuple, std::size_t... I>
constexpr T* make_new_from_tuple_impl(Tuple&& t, std::index_sequence<I...>)
{
static_assert(std::is_constructible_v<T,
decltype(std::get<I>(std::declval<Tuple>()))...>);
#if __cpp_lib_reference_from_temporary >= 202202L
if constexpr (std::tuple_size_v<std::remove_reference_t<Tuple>> == 1) {
using tuple_first_t = decltype(std::get<0>(std::declval<Tuple>()));
static_assert(!std::reference_constructs_from_temporary_v<T, tuple_first_t>);
}
#endif
return new T(std::get<I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template<class T, class Tuple>
constexpr T* make_new_from_tuple(Tuple&& t)
{
return detail::make_new_from_tuple_impl<T>(std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}
struct A
{
int i_; double d_; std::string s_;
A( int i, double d, const std::string& s ) : i_(i), d_(d), s_(s) {}
};
int main()
{
auto aTuple = std::make_tuple( 1, 1.5, std::string("Hello") );
auto a = make_new_from_tuple<A>(std::move(aTuple));
}
您可以使用std::apply
。它从元组解包参数并将其传递给可调用对象。您只需要包装构造函数。
#include <tuple>
#include <string>
struct A
{
int i_; double d_; std::string s_;
A( int i, double d, const std::string& s ) : i_(i), d_(d), s_(s) {}
};
int main() {
auto aTuple = std::make_tuple( 1, 1.5, std::string("Hello") );
A* a = std::apply( [](int i,double d,const std::string& s) { return new A(i,d,s);},aTuple);
}
为简洁起见,省略了完全转发。
直接调用new
:
T* obj = new auto(std::make_from_tuple<T>( { Args... args } ));
// unique ptr
std::unique_ptr obj{new auto(std::make_from_tuple<T>({ Args... args }))};
// shared ptr
std::shared_ptr obj{new auto(std::make_from_tuple<T>({ Args... args }))};
// doesn't work for make_shared unfortunately
auto obj = std::apply([](auto&&... args) {
return std::make_shared<T>(std::forward<decltype(args)>(args)...);
}, { Args... args });
因为make_from_tuple
返回一个右值,这将直接在堆中构造对象,而不需要复制或移动。
自c++ 11/c++ 14以来,显式使用new
和delete
被认为是一种不好的做法。因此,建议使用std::make_unique
(c++ 14)和std::make_shared
(c++ 11)。
template<typename T, typename...Args>
std::shared_ptr<T> make_shared_from_tuple(const std::tuple<Args...>& t)
{
return std::apply([](auto...args){ return std::make_shared<T>(args...); }, t);
}
template<typename T, typename...Args>
std::unique_ptr<T> make_unique_from_tuple(const std::tuple<Args...>& t)
{
return std::apply([](auto...args){ return std::make_unique<T>(args...); }, t);
}
https://godbolt.org/z/6zjTqK8f1
也许提供一些重载来处理move语义会更好。
我能写的最简单的是:
template<typename obj, typename tup>
auto unique_from_tuple(tup&& t) {
return std::apply(
[] (auto&& ... args) {
return std::make_unique<obj>
(std::forward<decltype(args)>(args)...);
},
std::forward<tup>(t)
);
};
// Create a unique pointer of type obj:
auto x =unique_from_tuple<obj>(y,z,w);