C++ 如何防止手动实例化所有模板类型?
我有一个函数C++ 如何防止手动实例化所有模板类型?,c++,templates,C++,Templates,我有一个函数Foo,它获取两个模板参数L3HdrType和L4HdrType。我收到一个数据包并对其进行解析,然后需要调用该函数。我的代码当前看起来像: ParsedPacket parsed_packet = parser.Parse(packet); if (parsed_packet.ip_version() == IPv4 && parsed_packet.ip_proto() == TCP) Foo(parsed_packet.L3Header<ipv4_hd
Foo
,它获取两个模板参数L3HdrType
和L4HdrType
。我收到一个数据包并对其进行解析,然后需要调用该函数。我的代码当前看起来像:
ParsedPacket parsed_packet = parser.Parse(packet);
if (parsed_packet.ip_version() == IPv4 && parsed_packet.ip_proto() == TCP)
Foo(parsed_packet.L3Header<ipv4_hdr>(), parsed_packet.L4Header<tcp_hdr>());
if (parsed_packet.ip_version() == IPv6 && parsed_packet.ip_proto() == TCP)
Foo(parsed_packet.L3Header<ipv6_hdr>(), parsed_packet.L4Header<tcp_hdr>());
if (parsed_packet.ip_version() == IPv4 && parsed_packet.ip_proto() == UDP)
Foo(parsed_packet.L3Header<ipv4_hdr>(), parsed_packet.L4Header<udp_hdr>());
if (parsed_packet.ip_version() == IPv6 && parsed_packet.ip_proto() == UDP)
Foo(parsed_packet.L3Header<ipv6_hdr>(), parsed_packet.L4Header<udp_hdr>());
注意,我的实际代码需要支持4种以上的情况,因此实际上代码比这里的示例要难看得多,因为if语句的数量随着头的数量呈指数增长。如果继承和运行时多态性不是一个选项,那么您可以通过一些技巧来解耦两个模板参数的推导涉及lambdas:
template <typename T> struct foo {};
template <typename T> struct bar {};
// the function to be called
template <typename A, typename B>
void foobar( foo<A> f, bar<B> b) {}
// bind the first parameter
template <typename T>
auto make_foofoo (foo<T> f) {
return [f](auto bar){ foobar(f,bar); };
}
// select second type here
template <typename F>
void do_the_actual_call(F f, int y) {
if (y == 1) f(bar<int>{});
if (y == 2) f(bar<double>{});
}
int main() {
// the "conditions"
int x = 1;
int y = 2;
// select first type here
if (x == 1) {
auto foofoo = make_foofoo(foo<int>{});
do_the_actual_call(foofoo,y);
} else if (x == 2){
auto foofoo = make_foofoo(foo<double>{});
do_the_actual_call(foofoo,y);
}
}
模板结构foo{};
模板结构条{};
//要调用的函数
模板
void foobar(foo f,bar b){}
//绑定第一个参数
模板
自动生成_foo(foo f){
返回[f](自动条){foobar(f,条);};
}
//在这里选择第二种类型
模板
void do_实际调用(F F,int y){
如果(y==1)f(bar{});
如果(y==2)f(bar{});
}
int main(){
//“条件”
int x=1;
int y=2;
//在这里选择第一种类型
如果(x==1){
autofoooo=make_foooo(foo{});
进行实际呼叫(foofoo,y);
}else如果(x==2){
autofoooo=make_foooo(foo{});
进行实际呼叫(foofoo,y);
}
}
它仍然是重复的代码,但它的扩展方式是
x+y
而不再是x*y
这里有一个替代解决方案,它可以工作,无论您必须进行许多参数选择,并且不需要运行时传递lambda/function对象
以下是一些通用的东西: …致:
typename std::enable_if</*expression*/>::type
呼叫
必须按此命名,并且必须是静态成员。其他一切都可以按您的意愿命名
参数包保持其原始顺序
在本例中,此选项并不特别值得,但正如您所说: […]我的实际代码需要支持4种以上的情况,因此 事实上,代码比这里的示例要难看得多,因为 if语句的数量随头数呈指数增长 …因此,此解决方案在实际情况下应提供良好的可扩展性/可维护性
.如果您需要将不同的参数组合传递给soemthing,那么我不会称之为“复制”。您需要将
if
s的条件映射到某个正确的参数组合。也许您可以保存一些字符,但“复制”将是相同的。为什么要手动指定模板类型?编译器应该能够从函数参数推断出它们。@NathanOliver是的,对不起,但我仍然需要所有的if语句。我将编辑这个问题。@idclev,我希望代码与枚举值的数量成线性增长,而不是指数增长(这里的2+2==2*2,所以它通常没有帮助)。所以,如果我能将IPv4映射到IPv4_hdr,将TCP映射到TCP_hdr,那就足够了。问题是IPv4到IPv4的映射在我的代码中出现了两次,这是重复的。@Elliott编辑。@Elliott我必须承认我以前没有仔细阅读过这个问题。现在它看起来真的是一个复制品。当然我不会重复发帖;),我会把这件事交给其他人来解决,不管是不是被骗了
template <typename T> struct foo {};
template <typename T> struct bar {};
// the function to be called
template <typename A, typename B>
void foobar( foo<A> f, bar<B> b) {}
// bind the first parameter
template <typename T>
auto make_foofoo (foo<T> f) {
return [f](auto bar){ foobar(f,bar); };
}
// select second type here
template <typename F>
void do_the_actual_call(F f, int y) {
if (y == 1) f(bar<int>{});
if (y == 2) f(bar<double>{});
}
int main() {
// the "conditions"
int x = 1;
int y = 2;
// select first type here
if (x == 1) {
auto foofoo = make_foofoo(foo<int>{});
do_the_actual_call(foofoo,y);
} else if (x == 2){
auto foofoo = make_foofoo(foo<double>{});
do_the_actual_call(foofoo,y);
}
}
#include <type_traits>
// We choose which arguments (Args...)
// to send to the Call method:
template <int N, int N_MAX, typename Caller, typename ... Args>
std::enable_if_t<N == N_MAX>
ChooseTemplateArgumentsRecursive (const bool[])
{
Caller::template Call<Args...>();
}
template <int N, int N_MAX, typename Caller, typename CandidateArg1, typename CandidateArg2, typename ... Args>
std::enable_if_t<N != N_MAX>
ChooseTemplateArgumentsRecursive (const bool choice[])
{
if (choice[N])
ChooseTemplateArgumentsRecursive<N+1, N_MAX, Caller, Args..., CandidateArg1>(choice);
else
ChooseTemplateArgumentsRecursive<N+1, N_MAX, Caller, Args..., CandidateArg2>(choice);
}
// You only need to call this function:
template <typename Caller, typename ... CandidateArgs>
void ChooseTemplateArguments (const bool choice[])
{
constexpr int N_MAX = sizeof...(CandidateArgs) / 2;
ChooseTemplateArgumentsRecursive<0, N_MAX, Caller, CandidateArgs...>(choice);
}
std::enable_if_t</*expression*/>
typename std::enable_if</*expression*/>::type
ChooseTemplateArguments<CallerFoo, ipv4_hdr, ipv6_hdr, tcp_hdr, udp_hdr>(choice);
struct CallerFoo
{
static ParsedPacket parsed_packet;
template <typename IpV, typename IpP>
static void Call ()
{
Foo(parsed_packet.L3Header<IpV>(), parsed_packet.L4Header<IpP>());
}
};
ParsedPacket CallerFoo::parsed_packet;