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;