C++14 元编程:在编译/初始化时自动构建类型列表

C++14 Metaprogramming: Automagically build a list of types at compile / init time

使用 C++14 和 Curiously Recurring Template Pattern (CRTP) 以及可能的 Boost.Hana(或 boost::mpl,如果您愿意)的某种组合,我可以在编译时构建类型列表吗没有显式声明的时间(或静态初始化时间)?

举个例子,我有这样的东西(见Coliru):

#include <iostream>
#include <boost/hana/tuple.hpp>
#include <boost/hana/for_each.hpp>

namespace
{
    struct D1 { static constexpr auto val = 10; };
    struct D2 { static constexpr auto val = 20; };
    struct D3 { static constexpr auto val = 30; };
}

int main()
{
    // How to avoid explicitly defining this?
    const auto list = boost::hana::tuple< D1, D2, D3 >{}; 

    // Do something with list
    boost::hana::for_each( list, []( auto t ) { std::cout << t.val << '\n'; } );
}

我想在创建 list 时避免使用明确的类型列表——D1D2D3,因为这意味着我有当我似乎应该能够在 class 声明 "Add this class to your running list" 中或周围告诉编译器时,手动维护该列表。 (我的最终目标是自动化工厂注册,这是缺失的机制。)

我可以使用一些继承 and/or 元编程技巧在编译时或静态初始化时编写列表吗?

听起来您想获得一个 compile-time 命名空间或其他范围内所有类型的元组。为此,您需要静态反射,它尚未添加到 C++ 中(但正如您所发现的那样非常有用)。您可以阅读 one proposal for static reflection here, and the N4428 proposal here.

作为解决方法,您可以编写一个宏来同时定义类型并在静态初始化期间将其隐式添加到注册表中。

我现在知道的唯一方法是如 here 所述的有状态元编程。但这很棘手,难以实施,委员会正试图将其排除在外。

要在 compile-time 完成它需要 "stateful" 元编程。在本文 here 中,Filip Roséen 解释了如何使用极其先进的 C++14 实现以下内容:

LX::push<void, void, void, void> ();
LX::set<0, class Hello> ();
LX::set<2, class World> ();
LX::pop ();

LX::value<> x; // type_list<class Hello, void, class World>

此外,Matt Calabrese 使用类似技术在 C++11 中实现 semantic-based 概念,请参阅幻灯片 #28 中的 video and slides

当然,这些技术依赖于支持一致 two-phase 名称查找的编译器。

或者,您可以重组代码以支持运行时注册,这要简单得多,并且可以跨 MSVC 等编译器移植。这就是 Prove or args 等库所使用的。它使用通用的 auto_register class:

template<class T, class F>
int auto_register_factory()
{
    F::template apply<T>();
    return 0;
}

template<class T, class F>
struct auto_register
{
    static int static_register_;
    // This typedef ensures that the static member will be instantiated if
    // the class itself is instantiated
    typedef std::integral_constant<decltype(&static_register_), &static_register_> static_register_type_;
};

template<class T, class F>
int auto_register<T, F>::static_register_ = auto_register_factory<T, F>();

那你就可以自己写CRTP了class:

struct foo_register
{
    template<class T>
    static void apply()
    {
        // Do code when it encounters `T`
    }
};

template<class Derived>
struct fooable : auto_register<Derived, foo_register>
{};