Variadic constexpr 类型选择器

Variadic constexpr type-selector

抱歉,这个名字太夸张了,我想创建一个 constexpr 函数,它接受可变数量的 布尔值 模板参数,以及 returns第一个 true 值的 "template index",在 C++11 中(仅欢迎 C++14 解决方案,但不会被接受为答案)。

比如调用这个函数Selector

Selector< false, false >() == 0 // none of the template argument is true
Selector< true, false, true >() == 1 // first true template argument is the first one
Selector< false, false, true, false >() == 3 // .. and here it's the third one

这个的典型用法,以及我称之为 "type-selector" 的原因是

Selector< std::is_pointer<T>::value, std::is_arithmetic<T>::value >()

我希望它成为 constexpr 的原因是用于部分模板专业化。

虽然我认为使用可变参数模板、constexpr 模板特化(对于 0 的情况)和递归(是否有可能 "consume" 模板参数,但我不确定该怎么做,像 bash 中的 shift?),这应该是可行的。

#include <cstddef>
#include <type_traits>

template <std::size_t I, bool... Bs>
struct selector;

template <std::size_t I, bool... Bs>
struct selector<I, true, Bs...> : std::integral_constant<std::size_t, I> {};

template <std::size_t I, bool... Bs>
struct selector<I, false, Bs...> : selector<I+1, Bs...> {};

template <std::size_t I>
struct selector<I> : std::integral_constant<std::size_t, 0> {};

template <bool... Bs>
constexpr std::size_t Selector()
{
    return selector<1, Bs...>::value;
}

DEMO

我不满意答案不是纯粹的 constexpr 函数。所以我在递归中重新编写了它,一个 C++11 兼容的编译器将接受:

#include <cstddef>

template<std::size_t I = 1>
constexpr std::size_t selector(bool value = false){
    return value ? I : 0;
}

template<std::size_t I = 1, typename ... Bools>
constexpr std::size_t selector(bool first, bool second, Bools ... others){
    return first ? I : selector<I+1>(second, others...);
}

这将使用函数式语法而不是模板来调用,并且将始终是 constexpr 因为模板参数递增。

根据@CoffeeandCode 的回答,这是另一个使用 constexpr 递归按预期工作的示例:

#include <iostream>
#include <cstddef>

template<bool B0=false, bool... Bs>
constexpr std::size_t Selector( std::size_t I = 1 )
{
    return B0 ? I : Selector<Bs...>(I+1);
}

template<>
constexpr std::size_t Selector<false>( std::size_t I )
{
    return 0;
}

int main()
{
    std::cout<< Selector() << std::endl;
    std::cout<< Selector<false,false>() << std::endl;
    std::cout<< Selector<true,false,true>() << std::endl;
    std::cout<< Selector<false,false,true,false>() << std::endl;
}