处理两个或更多模板参数的最佳方法是什么?

3

假设我有以下形式的模板函数:

template<bool a, bool b, bool c>
void foo(){
   if(a) printf("I do something when a is true!\n");
   if(b) printf("I do something when b is true!\n");
   if(c) printf("I do something when c is true!\n");
}

现在我有一个函数,可以在编译时为8个可能的情况(a = b = c = true、a=b=true c=false等)进行特化。
我想用运行时获取的a、b和c的值来调用这个函数。
如果模板只有一个参数,我可以这样做:
void caller(bool a){
   if(a) foo<true>();
   else  foo<false>();
}

但是如果您有两个或更多参数呢?您不能只这样做:

void caller(bool a, bool b, bool c){
   foo<a,b,c>();
}

如果您使用if-else/switch进行编程,那么可能会出现繁琐的代码和混乱的情况。我希望编译器可以编译8个版本的foo并直接调用。

foo<a,b,c>()

如果我不是布尔类型而是其他类型,情况也可以等价地被处理:
template<int a>
void foo(){
   printf("%d", a);
} 

假设我知道a的取值范围在0到32之间。

我想要这样做:

void caller(int a){
   if(a<=32 && a>0) foo<a>();
   else printf("ERROR!/n");
}

如何处理这种情况最好的方式是什么?

1
最好使用constexpr函数而不是模板。http://en.cppreference.com/w/cpp/language/constexpr - jamek
…那么最简单的方法不就是将原始函数中的每个if分支变成自己的基于布尔类型的模板函数,然后从三个模板参数函数的编译时和运行时版本中调用它们吗?我猜你实现的各种函数并不是constexpr的... - jaggedSpire
1
为了使foo<a,b,c>()正常工作,必须在编译时知道a、b、c的值。 - Schtolc
5
将foo变为非模板函数?你真正想要实现什么?你是如何到达需要将运行时值注入到编译时常量的这一点的? - YSC
你正在混淆运行时参数和编译时参数,如果你不先清除前者,那么深入了解constexpr只会让情况变得更糟。 - Marco A.
显示剩余2条评论
2个回答

5

但是如果你有两个或更多参数呢?你不能只这样做:

你可以使用可变参数模板、递归、高阶函数和 std::integral_constant

template <typename TF>
auto with_bool_constant(TF xf)
{
    return xf();
};

template <typename TF, typename T, typename... Ts>
auto with_bool_constant(TF xf, T x, Ts... xs)
{
    if(x) 
        return with_bool_constant([&](auto... ys)
                                  { xf(std::integral_constant<bool, true>{}, ys...); }, 
                                  xs...);
    else 
        return with_bool_constant([&](auto... ys)
                                  { xf(std::integral_constant<bool, false>{}, ys...); }, 
                                  xs...);
};

使用方法:

template <bool a, bool b, bool c>
void f() 
{ 
    std::cout << std::boolalpha << a << " " << b << " " << c << "\n"; 
}

int main()
{
    auto f_wrapper = [](auto a, auto b, auto c)
    {
        return f<a, b, c>();
    };

    with_bool_constant(f_wrapper, true, false, true);
}

wandbox示例


假设我知道a的取值范围在0到32之间。我想要做如下操作:

您可以使用boost::hana::make_range生成一个0...32的编译时范围(边界必须在编译时已知)。然后,您可以使用boost::hana::for_each或类似的编译时迭代结构将运行时值转换为std::integral_constant<int,X>,并应用与我上面发布的类似技术。


2
您的想法是正确的,您只需要针对每个参数进行准递归处理:
#include <cstdio>

template <bool a, bool b, bool c>
void foo() {
     if(a) printf("I do something when a is true!\n");
     if(b) printf("I do something when b is true!\n");
     if(c) printf("I do something when c is true!\n");
}

template <bool... bs>
void caller() {
    foo<bs...>();
}

template <bool... cbs, typename... RBS>
void caller(bool rb0, RBS... rbs) {
    if (rb0) {
        caller<cbs..., true>(rbs...);
    } else {
        caller<cbs..., false>(rbs...);
    }
}

int main() {
     caller(true, false, true);
}

出于好奇,您能否确认或否认我的怀疑,即编译 caller(a,b,c) 是否会导致实例化所有可能的 foo() 变体? - user3458
1
@Arkadiy 是的,对于任何从编译时到运行时值的映射都是如此。 - yuri kilochek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接