>我正在尝试递归地运行类型列表,以便我可以根据列表中的每种类型执行一些运行时代码。我希望能够在结构中的函数(而不是结构中的函数(中的元组中递归运行所有类型,而无需使用"if constexpr"来终止递归。
我有一段代码显示了使用 constexpr 的递归。
#include <iostream>
#include <string>
#include <tuple>
template <typename ...Ts>
struct temp{
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this()
{
_inner_print<_N,_N>();
}
template <std::size_t N, std::size_t MAX>
void _inner_print()
{
if constexpr ( N != 0 )
{
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
}
}
TypeList _mem;
};
int main()
{
std::string name;
temp<int, int, int> t1;
t1.print_this();
}
我希望能够对 C++14 执行相同的递归,而不是 C++17 w/"if constexpr"。
谢谢!
是使用index_sequence
。
这是一个 C++14 的工作解决方案,使用@MartinMorterol建议进行了改进。
// -*- compile-command: "g++ -Wall -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
{
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() { _inner_print(std::make_index_sequence<_N>()); }
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
{
auto dummy = {0, (_inner_print<IDX>(),0)...};
(void)dummy;
}
template <std::size_t IDX>
void _inner_print()
{
std::cout << "nCall #" << IDX
<< " sizeof " << sizeof(std::get<IDX>(_mem));
}
TypeList _mem;
};
int main()
{
std::string name;
temp<int, double, char> t1;
t1.print_this();
}
其中打印:
g++ -Wall -std=c++14 poub.cpp; ./a.out
Call #0 sizeof 4
Call #1 sizeof 8
Call #2 sizeof 1
我的初始答案(使用递归(
// -*- compile-command: "g++ -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
{
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() { _inner_print(std::make_index_sequence<_N>()); }
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
{
_inner_print(std::integral_constant<std::size_t, IDX>()...);
}
template <std::size_t HEAD_IDX, typename... TAIL>
void _inner_print(std::integral_constant<std::size_t, HEAD_IDX>, TAIL... tail)
{
std::cout << "nCall #" << HEAD_IDX
<< " sizeof " << sizeof(std::get<HEAD_IDX>(_mem));
// whatever you want HERE ...
_inner_print(tail...);
}
void _inner_print(){};
TypeList _mem;
};
int main()
{
std::string name;
temp<int, double, char> t1;
t1.print_this();
}
如果可以将_inner_print
函数更改为类,则可以使用部分专用化来结束递归:
template <std::size_t N, std::size_t MAX>
struct _inner_print
{
_inner_print()
{
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
}
};
template <std::size_t MAX> struct _inner_print<0, MAX> { };
它不是将_inner_print()
作为函数调用,而是成为未命名临时的声明,调用执行输出的构造函数。
您应该使用部分专用化。但是你不能用函数来做到这一点。
您应该使用struct
来解决问题。
#include <iostream>
#include <string>
#include <tuple>
template <std::size_t N, std::size_t MAX, class T>
struct inner_print_impl{
static void run(const T& caller)
{
std::cout << "Call #"<<MAX-N<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
inner_print_impl<N-1, MAX , T>::run(caller);
}
};
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>{
static void run(const T& caller)
{
std::cout << "Call #"<<MAX<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
// no recursion
}
};
template <typename ...Ts>
struct temp{
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t N_ = std::tuple_size<TypeList>::value;
template <std::size_t N, std::size_t MAX, class T>
friend struct inner_print_impl;
void print_this()
{
inner_print_impl<N_,N_, temp<Ts...> >::run(*this);
}
TypeList _mem;
private :
int a ; // test acces
};
int main()
{
std::string name;
temp<int, int, int> t1;
t1.print_this();
}
注意:
- 如果您需要访问私人 membre,则需要将
*this
传递给呼叫并将新struct
添加为班级的朋友 - 在我的代码中,我在
/* other dynamic code */
部分有一个重复项。您可以 :- 使用函数
- 将模板参数设置为
int
而不是size_t
,并在-1
而不是0
处停止
附言 :
我不明白这个部分
在结构中的函数中的元组中(不在结构中的函数中(
我希望我没有错过什么
编辑 :
我的代码比你的代码多做一次迭代,你可以清空这个:
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>{
static void run(const T& caller)
{
}
};
而且您不会显示0
案例。
template<class Integral, Integral N>
auto dispatch( std::integral_constant<Integral, N> ) {
return [](auto&&...args){
return std::get<N>( std::forward_as_tuple( decltype(args)(args)... ) );
};
}
template<std::size_t N>
auto dispatch() {
return dispatch( std::integral_constant<std::size_t, N>{} );
}
这个小美女给你带来了 C++14 中if constexpr
的魔力。
template <std::size_t N, std::size_t MAX>
void _inner_print() {
dispatch( std::integral_constant<bool, N==0>{} )
(
// 0, aka false branch:
[&](auto Nval){
std::cout << "Call #"<<MAX-Nval<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<Nval-1, MAX>();
},
// 1, aka true branch:
[](auto&&){}
)( std::integral_constant<std::size_t, N>{} );
}
活生生的例子。
请注意,我们必须通过参数将N
传递给 lambda,因为我们希望 lambda 主体的有效性根据其参数而变化,而参数仅传递给正确的参数。
或者,您可以使用overload
帮助程序:
template<class T0, class...Ts>
struct overloaded;
template<class Lhs, class Rhs, class...Ts>
struct overloaded<Lhs, Rhs, Ts...>:
overloaded<Lhs>, overloaded<Rhs,Ts...>
{
using overloaded<Lhs>::operator();
using overloaded<Rhs,Ts...>::operator();
template<class A0, class...As>
explicit overloaded(A0&&a0, As&&...as) :
overloaded<Lhs>(std::forward<A0>(a0)),
overloaded<Rhs,Ts...>( std::forward<As>(as)... )
{}
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
};
template<class T0>
struct overloaded<T0> : T0 {
using T0::operator();
template<class A0>
explicit overloaded(A0&&a0):
T0(std::forward<A0>(a0))
{}
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
};
template<class R, class...Args>
struct overloaded<R(*)(Args...)> {
R operator()(Args... args) const {
return pf(std::forward<Args>(args)...);
}
R(*pf)(Args...);
overloaded( R(*f)(Args...) ):pf(f) {}
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
};
template<class...Args>
auto overload( Args&&...args ) {
return overloaded<std::decay_t<Args>...>(
std::forward<Args>(args)...
);
}
以下是如何使用overloaded
:
template <std::size_t N, std::size_t MAX>
void _inner_print()
{
std::integral_constant<std::size_t, N> index;
overload(
[](std::integral_constant<std::size_t, 0>) {},
[&](auto index) {
std::cout << "Call #"<<MAX-index<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<index-1, MAX>();
}
)(index);
}
请注意,规则是有时仅编译的动态代码必须依赖于 Lambda 的auto
参数。
活生生的例子。