在不知道类型的情况下初始化元组中的智能指针
Initializing smart pointers in a tuple without knowing the type
我有一个需要初始化的智能指针元组(作为 class 模板的成员)。我使用 std::apply 在其他地方迭代元组,但是如何在不知道它们的类型的情况下用新对象初始化它们? 运行 下面带有调试器的代码告诉我元组中的元素之后仍然是“空的”。我在这里做错了什么?
struct A {
int a = 1;
}
struct B {
int b = 2;
}
std::tuple< std::unique_ptr< A >, std::unique_ptr< B > > my_tuple;
std::apply( [] ( auto&... ptr ) { //std::unique_ptr< T >
(..., ptr.reset( { } )); //ptr.reset( new T() )
}, my_tuple );
如果您只想将唯一指针重置为类型的默认值,您可以使用如下所示的数字索引迭代元组中的值
#include <tuple>
#include <memory>
#include <iostream>
struct A {
int u;
A() : u(5) {};
};
struct B {
int v;
B() : v(6) {};
};
template<size_t I = 0, typename... Tp>
void reset_tuple_of_unique_ptrs(std::tuple<Tp...>& t) {
auto& item = std::get<I>(t);
using T = typename std::remove_reference_t<decltype(item)>::element_type;
item.reset( new T() );
if constexpr (I + 1 != sizeof...(Tp))
reset_tuple_of_unique_ptrs<I + 1>(t);
}
int main() {
std::tuple<std::unique_ptr<A>, std::unique_ptr<B>> tup = { std::make_unique<A>(), std::make_unique<B>() };
std::get<0>(tup)->u = 42;
std::get<1>(tup)->v = 17;
std::cout << std::get<0>(tup)->u << " , " << std::get<1>(tup)->v << "\n";
reset_tuple_of_unique_ptrs(tup);
std::cout << std::get<0>(tup)->u << " , " << std::get<1>(tup)->v << "\n";
}
如评论中所述,您可以将 decltype
应用于 ptr
以获取 unique_ptr
的类型,然后对其应用 element_type
:
std::apply([](auto &... ptr)
{
((ptr = std::make_unique<typename std::remove_reference_t<decltype(ptr)>::element_type>()), ...);
}, my_tuple );
(我用 make_unique
替换了 new
,并将 ...
移到了最后,但这些只是样式的变化。)
这可以用 C++20 模板 lambda 来缩短:
std::apply([]<typename ...P>(std::unique_ptr<P> &...ptrs )
{
((ptrs = std::make_unique<P>()), ...);
}, my_tuple );
我不是 100% 确定您想要支持哪些用例,因此我制作了一些可能适合您尝试执行的功能模板。
#include <memory>
#include <tuple>
#include <type_traits>
// create a tuple of unique_ptr's pointing to default constructed objects
template<class... Ts>
std::tuple<std::unique_ptr<Ts>...> init_up_tuple() {
return std::make_tuple(std::make_unique<Ts>()...);
}
// create a tuple of unique_ptr's with copies of the supplied objects
template<class... Ts>
std::tuple<std::unique_ptr<Ts>...> init_up_tuple(const Ts&... ts) {
return std::make_tuple(std::make_unique<Ts>(ts)...);
}
// create a tuple of unique_ptr's with default constructed objects of the same
// types as those in the supplied tuple
template<class... Ts>
std::tuple<std::unique_ptr<Ts>...> init_up_tuple(
const std::tuple<std::unique_ptr<Ts>...>&)
{
return std::make_tuple(std::make_unique<Ts>()...);
}
namespace detail {
template<class... Ts, size_t... Idx>
std::tuple<std::unique_ptr<Ts>...> copy_up_tuple_impl(
const std::tuple<std::unique_ptr<Ts>...>& tu,
std::index_sequence<Idx...>)
{
return std::make_tuple(std::make_unique<Ts>(*std::get<Idx>(tu))...);
}
} // namespace detail
// create a tuple of unique_ptr's pointing to copy constructed objects
// from the objects in the supplied tuple of unique_ptr's
template<class... Ts, typename Indices = std::index_sequence_for<Ts...>>
std::tuple<std::unique_ptr<Ts>...> copy_up_tuple(
const std::tuple<std::unique_ptr<Ts>...>& tu)
{
return detail::copy_up_tuple_impl(tu, Indices{});
}
然后可以像这样使用它们:
#include <iostream>
struct A {
int a = 1;
};
struct B {
int b = 2;
};
int main() {
auto t1 = init_up_tuple<A, B>(); // default constructed
std::cout << std::get<0>(t1)->a << ' ' << std::get<1>(t1)->b << '\n';
A a{3};
auto t2 = init_up_tuple(a, B{4}); // copy construct from the supplied objects
std::cout << std::get<0>(t2)->a << ' ' << std::get<1>(t2)->b << '\n';
auto t3 = copy_up_tuple(t2); // copy construct from tuple of unique_ptr's
std::cout << std::get<0>(t3)->a << ' ' << std::get<1>(t3)->b << '\n';
t3 = init_up_tuple(t3); // reset to default
std::cout << std::get<0>(t3)->a << ' ' << std::get<1>(t3)->b << '\n';
}
输出:
1 2
3 4
3 4
1 2
我有一个需要初始化的智能指针元组(作为 class 模板的成员)。我使用 std::apply 在其他地方迭代元组,但是如何在不知道它们的类型的情况下用新对象初始化它们? 运行 下面带有调试器的代码告诉我元组中的元素之后仍然是“空的”。我在这里做错了什么?
struct A {
int a = 1;
}
struct B {
int b = 2;
}
std::tuple< std::unique_ptr< A >, std::unique_ptr< B > > my_tuple;
std::apply( [] ( auto&... ptr ) { //std::unique_ptr< T >
(..., ptr.reset( { } )); //ptr.reset( new T() )
}, my_tuple );
如果您只想将唯一指针重置为类型的默认值,您可以使用如下所示的数字索引迭代元组中的值
#include <tuple>
#include <memory>
#include <iostream>
struct A {
int u;
A() : u(5) {};
};
struct B {
int v;
B() : v(6) {};
};
template<size_t I = 0, typename... Tp>
void reset_tuple_of_unique_ptrs(std::tuple<Tp...>& t) {
auto& item = std::get<I>(t);
using T = typename std::remove_reference_t<decltype(item)>::element_type;
item.reset( new T() );
if constexpr (I + 1 != sizeof...(Tp))
reset_tuple_of_unique_ptrs<I + 1>(t);
}
int main() {
std::tuple<std::unique_ptr<A>, std::unique_ptr<B>> tup = { std::make_unique<A>(), std::make_unique<B>() };
std::get<0>(tup)->u = 42;
std::get<1>(tup)->v = 17;
std::cout << std::get<0>(tup)->u << " , " << std::get<1>(tup)->v << "\n";
reset_tuple_of_unique_ptrs(tup);
std::cout << std::get<0>(tup)->u << " , " << std::get<1>(tup)->v << "\n";
}
如评论中所述,您可以将 decltype
应用于 ptr
以获取 unique_ptr
的类型,然后对其应用 element_type
:
std::apply([](auto &... ptr)
{
((ptr = std::make_unique<typename std::remove_reference_t<decltype(ptr)>::element_type>()), ...);
}, my_tuple );
(我用 make_unique
替换了 new
,并将 ...
移到了最后,但这些只是样式的变化。)
这可以用 C++20 模板 lambda 来缩短:
std::apply([]<typename ...P>(std::unique_ptr<P> &...ptrs )
{
((ptrs = std::make_unique<P>()), ...);
}, my_tuple );
我不是 100% 确定您想要支持哪些用例,因此我制作了一些可能适合您尝试执行的功能模板。
#include <memory>
#include <tuple>
#include <type_traits>
// create a tuple of unique_ptr's pointing to default constructed objects
template<class... Ts>
std::tuple<std::unique_ptr<Ts>...> init_up_tuple() {
return std::make_tuple(std::make_unique<Ts>()...);
}
// create a tuple of unique_ptr's with copies of the supplied objects
template<class... Ts>
std::tuple<std::unique_ptr<Ts>...> init_up_tuple(const Ts&... ts) {
return std::make_tuple(std::make_unique<Ts>(ts)...);
}
// create a tuple of unique_ptr's with default constructed objects of the same
// types as those in the supplied tuple
template<class... Ts>
std::tuple<std::unique_ptr<Ts>...> init_up_tuple(
const std::tuple<std::unique_ptr<Ts>...>&)
{
return std::make_tuple(std::make_unique<Ts>()...);
}
namespace detail {
template<class... Ts, size_t... Idx>
std::tuple<std::unique_ptr<Ts>...> copy_up_tuple_impl(
const std::tuple<std::unique_ptr<Ts>...>& tu,
std::index_sequence<Idx...>)
{
return std::make_tuple(std::make_unique<Ts>(*std::get<Idx>(tu))...);
}
} // namespace detail
// create a tuple of unique_ptr's pointing to copy constructed objects
// from the objects in the supplied tuple of unique_ptr's
template<class... Ts, typename Indices = std::index_sequence_for<Ts...>>
std::tuple<std::unique_ptr<Ts>...> copy_up_tuple(
const std::tuple<std::unique_ptr<Ts>...>& tu)
{
return detail::copy_up_tuple_impl(tu, Indices{});
}
然后可以像这样使用它们:
#include <iostream>
struct A {
int a = 1;
};
struct B {
int b = 2;
};
int main() {
auto t1 = init_up_tuple<A, B>(); // default constructed
std::cout << std::get<0>(t1)->a << ' ' << std::get<1>(t1)->b << '\n';
A a{3};
auto t2 = init_up_tuple(a, B{4}); // copy construct from the supplied objects
std::cout << std::get<0>(t2)->a << ' ' << std::get<1>(t2)->b << '\n';
auto t3 = copy_up_tuple(t2); // copy construct from tuple of unique_ptr's
std::cout << std::get<0>(t3)->a << ' ' << std::get<1>(t3)->b << '\n';
t3 = init_up_tuple(t3); // reset to default
std::cout << std::get<0>(t3)->a << ' ' << std::get<1>(t3)->b << '\n';
}
输出:
1 2
3 4
3 4
1 2