c++ - STD::Tuple 实现 C++
问题描述
参考下面的代码,我发现很难掌握元模板编程中递归和平面 STD::tuple 实现之间的主要区别。如果可能的话,我希望任何人都能够解释两者之间的核心区别,为什么应该首选一个而不是另一个?另外,如果可能的话,解释一下每个人是如何为像我这样刚接触元模板编程的初学者工作的。我希望下面的代码能够帮助那些想知道 STD::tuple 在元模板编程中的实现的人。
#include <tuple>
#include <iostream>
#include <utility>
namespace flop
{
namespace recursion
{
template <typename...>
class leaf{};
template <typename CArg,typename... CArgs>
class leaf<CArg, CArgs...> :
public leaf<CArgs...>
{
public:
template <typename Targ,typename... Targs>
leaf(Targ&& num, Targs&&... nums) :
leaf<CArgs...>(std::forward<Targs>(nums)...),
_value{std::forward<Targ>(num)}
{
}
CArg _value;
};
template <typename... Ts>
using tuple = leaf<Ts...>;
template <size_t N,typename CArg,typename... CArgs>
class simplify_pack
{
public:
using type = typename simplify_pack<N - 1, CArgs...>::type;
};
template <
typename CArg,
typename... CArgs
>
class simplify_pack<0, CArg, CArgs...>
{
public:
using type = leaf<CArg, CArgs...>;
};
template <
size_t N,
typename... Ts
>
using simplify_pack_t = typename simplify_pack<N, Ts...>::type;
template <
size_t N,
typename... Ts
>
auto& get(tuple<Ts...>& t)
{
using base = simplify_pack_t<N, Ts...>;
return static_cast<base&>(t)._value;
}
}
///////////////////////////////////////////////////////////////////////////////
namespace flat
{
template <size_t N,typename T>
class leaf
{
public:
template <typename Targ>
explicit leaf(Targ&& num) :
_value{std::forward<Targ>(num)}
{
}
T _value;
};
template <typename Seq,typename... Ts>
class tuple_implem{};
template <size_t... Ns,typename... Ts>
class tuple_implem<std::index_sequence<Ns...>,Ts...>
: public leaf<Ns, Ts>...
{
public:
template <typename... Targs>
tuple_implem(Targs&&... nums) :
leaf<Ns, Ts>{std::forward<Targs>(nums)}...
{
}
};
template <typename... Ts>
using tuple = tuple_implem<
std::make_index_sequence< sizeof...(Ts) >,
Ts...
>;
template <size_t N,typename CArg,typename... CArgs>
auto N_type_search_f()
{
if constexpr (N == 0)
{
return CArg{};
}
else
{
return N_type_search_f<N - 1, CArgs...>();
}
}
template <
size_t N,
typename... Ts
>
using N_type_search = decltype( N_type_search_f<N, Ts...>() );
template <
size_t N,
typename... Ts
>
N_type_search<N, Ts...>& get(tuple<Ts...>& t)
{
using base = leaf<N, N_type_search<N, Ts...> >;
return static_cast<base&>(t)._value;
}
}
}
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::cout << "std::tuple" << std::endl;
{
namespace myn_s = std;
myn_s::tuple<int, int, double> t{10, 20, 30.0};
auto print_tuple = [&]()
{
std::cout
<< "\tFirst:\t" << myn_s::get<0>(t) << "\n"
<< "\tSecond:\t" << myn_s::get<1>(t) << "\n"
<< "\tThird:\t" << myn_s::get<2>(t) << std::endl;
};
print_tuple();
myn_s::get<0>(t) += 100;
myn_s::get<1>(t) += 200;
myn_s::get<2>(t) += 300;
print_tuple();
}
std::cout << "flop::recursion::tuple" << std::endl;
{
namespace myn_s = flop::recursion;
myn_s::tuple<int, int, double> t{10, 20, 30.0};
auto print_tuple = [&]()
{
std::cout
<< "\tFirst:\t" << myn_s::get<0>(t) << "\n"
<< "\tSecond:\t" << myn_s::get<1>(t) << "\n"
<< "\tThird:\t" << myn_s::get<2>(t) << std::endl;
};
print_tuple();
myn_s::get<0>(t) += 100;
myn_s::get<1>(t) += 200;
myn_s::get<2>(t) += 300;
print_tuple();
}
std::cout << "flop::flat::tuple" << std::endl;
{
namespace myn_s = flop::flat;
myn_s::tuple<int, int, double> t{10, 20, 30.0};
auto print_tuple = [&]()
{
std::cout
<< "\tFirst:\t" << myn_s::get<0>(t) << "\n"
<< "\tSecond:\t" << myn_s::get<1>(t) << "\n"
<< "\tThird:\t" << myn_s::get<2>(t) << std::endl;
};
print_tuple();
myn_s::get<0>(t) += 100;
myn_s::get<1>(t) += 200;
myn_s::get<2>(t) += 300;
print_tuple();
}
}
解决方案
为什么一个应该优先于另一个?
递归实现符合 C++11,因此当更高版本的标准不可用时应该首选它,因为它是唯一的选择。所示的平面实现通过 using 依赖于 C++17,通过 using 依赖于 C if constexpr
++14 std::index_sequence
。
在我看来,平面实现更简单。另外,我怀疑编译起来会更快,尽管我建议测量以验证它是否正确。因此,当有足够的标准级别可用时,我更喜欢平面实现。
推荐阅读
- windows - 如何从文件名创建文件夹并将文件移动到文件夹中?
- c# - Linq 查询未在 MVC 应用程序中转换为字符串
- c# - 如何使用 selectedIndexChanged 访问工作表数据?
- sql - 从行组合中查找重复项
- ssl - 当负载均衡器终止 SSL 连接时,它是否会创建到目标服务器的新 TCP 连接?
- python - 直接向gunicorn发送请求
- raspberry-pi - Raspbian 无法正确更新
- android - Android Room 迁移未正确处理
- cassandra - 如何定期修复集群,-local或-pr一一修复
- android - 如何在 android OnKeyPress Logic 中执行此操作