枚举值的编译时列表

Compile-time list of enum values

提问人:sbi 提问时间:6/5/2018 最后编辑:sbi 更新时间:6/6/2018 访问量:4626

问:

在现代 C++(GCC 5.1.0,所以 C++14,我猜),在编译时传递 的值列表,然后在运行时检查其中有哪些值的最快方法是什么?enum

enum foobar { foo, bar, baz };

template<????>
void f() {
  if( contains<????, foo>() )
    std::cout << "foo!";
  if( contains<????, bar>() )
    std::cout << "bar!";
  if( contains<????, baz>() )
    std::cout << "baz!";
}

f<foo,bar>();

注意:这是针对单元测试的,因此速度等主要是无关紧要的,主要目标是让不熟悉代码的人可以破译它。

C++ C++14 variadic-templates 模板元编程

评论

0赞 Bathsheba 6/5/2018
可能可以通过 .enum class
1赞 sbi 6/5/2018
@Bathsheba:扔给我一根骨头,你做到了。
0赞 6/5/2018
我不确定我是否理解这个问题。您想检查给定的枚举类型内部是否有给定的值名称,这是正确的吗?
0赞 sbi 6/5/2018
@Frank:“枚举值列表” 我在那里放了一个用法示例。
0赞 6/5/2018
@sbi,所以,基本上是一个?std::integer_sequence<foobar, foo, bar>

答:

3赞 Mikhail 6/5/2018 #1

这里有一个建议

#include <initializer_list>// pulled in by a lot of stuff

enum class options  { foo,bar,baz };
void test_func(options opt)
{

}
int main()
{
    auto test_vector = { options::foo, options::bar  };
    for (auto option : test_vector)
    {
        test_func(option);
    }
    return 0;
}

检查提供的测试向量是否包含它们应包含的内容稍微复杂一些:

#include <initializer_list>
#include <algorithm>
#include <stdexcept>

enum class options { foo, bar, baz, wuz };
void test_func(options)
{

}

template<typename AT, typename BT>
void assert_test_vectors(AT a, BT check_these_items)
{
    for (auto item : check_these_items)
    {
        auto test = std::find(a.begin(), a.end(), item) != a.end();
        if (!test)
        {
            return throw std::runtime_error("You suck");
        }
    }
}

template<typename T>
void run_tests(T tests)
{
    const auto better_have_these = { options::foo, options::bar };
    assert_test_vectors(tests, better_have_these);
    for (auto test : tests)
    {
        test_func(test);
    }
}

int main()
{
    const auto test_vectors = { options::foo, options::wuz };
    run_tests(test_vectors);
    return 0;
}

评论

0赞 sbi 6/5/2018
我接受这个答案是因为,多亏了 Mifhail 提出的一个问题,事实证明我不需要在编译时传递枚举值,最终这就是我的解决方案所基于的。
1赞 login_not_failed 6/5/2018 #2

可变参数选项。正如预期的那样,它打印了«foo!bar!baz!foo!»。

enum class foobar { foo, bar, baz };

void test() {}

template< typename F, typename... Fs >
void test( F Foo, Fs const&... args )
{
    switch( Foo )
    {
    case foobar::foo:
        std::cout << "foo!";
        break;
    case foobar::bar:
        std::cout << "bar!";
        break;
    case foobar::baz:
        std::cout << "baz!";
        break;
    }
    test( args... );
}

int main() {
    test( foobar::foo, foobar::bar, foobar::baz, foobar::foo );
}
2赞 max66 6/5/2018 #3

我不知道你是否还感兴趣,但是......要回答您最初的问题...如果将模板参数的顺序切换为 ...我的意思是contain

if( contains<foo, ????>() )

而不是

if( contains<????, foo>() )

从C++14开始,你的(在我下面的例子中重命名)可以是一个简单的函数containscontainsFBconstexpr

template <foobar F0, foobar ... Fs>
constexpr bool containsFB ()
 {
   using unused = bool[];

   bool ret { false };

   (void)unused { false, ret |= F0 == Fs ... };

   return ret;
 }

从 C++17 开始,您还可以使用模板折叠,并且可以编写为继承自 或 的类型特征。constantFBstd::true_typestd::false_type

template <foobar F0, foobar ... Fs>
struct containsFB : public std::integral_constant<bool, ((F0 == Fs) || ...)>
 { };

下面是一个完整的 C++14 示例

#include <iostream>
#include <utility>

enum foobar { foo, bar, baz };


template <foobar F0, foobar ... Fs>
constexpr bool containsFB ()
 {
   using unused = bool[];

   bool ret { false };

   (void)unused { false, ret |= F0 == Fs ... };

   return ret;
 }

/* C++17 alternative
template <foobar F0, foobar ... Fs>
struct containsFB : public std::integral_constant<bool, ((F0 == Fs) || ...)>
 { };
 */

template <foobar ... Fs>
void f ()
 {
   if( containsFB<foo, Fs...>() ) std::cout << "foo!";
   if( containsFB<bar, Fs...>() ) std::cout << "bar!";
   if( containsFB<baz, Fs...>() ) std::cout << "baz!";
 }

int main ()
 {
   f<foo,bar>();
 }

如果你想使(函数或结构)更通用一些,你可以模板化值的类型;所以containsFB

template <typename T, T F0, T ... Fs>

而不是

template <foobar F0, foobar ... Fs>