首页 > 解决方案 > 不使用有状态元编程的 C++ 计数类型

问题描述

我正在尝试通过使用宏注册类型来逐步创建类型列表。为此,我想使用我发现的一个技巧来回答另一个问题。精简后的代码如下:

#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>

#define MAXIMUM_SIZE 100

template <std::size_t N>
struct Index : Index<N - 1> {};
template <>
struct Index<0> {};

std::tuple<> GetTypes(Index<0>) { return {}; }

#define GET_REGISTERED_TYPES \
  decltype(GetTypes(std::declval<Index<MAXIMUM_SIZE>>()))

#define REGISTER_TYPE(Type)                              \
    inline decltype(std::tuple_cat( \
        std::declval<GET_REGISTERED_TYPES>(), \
        std::declval<std::tuple<Type>>()))        \
  GetTypes(Index<std::tuple_size_v<GET_REGISTERED_TYPES> + 1>) { \
    return {};                                                \
  }
REGISTER_TYPE(int)
REGISTER_TYPE(float)
REGISTER_TYPE(char)
REGISTER_TYPE(Index<78>)
int main() {
    // true
    std::cout << std::boolalpha 
        << std::is_same_v<
            GET_REGISTERED_TYPES,
            std::tuple<int, float, char, Index<78>>> 
        << std::endl;
}

这种代码是否被认为可以安全使用,或者它是语言某些晦涩部分的一部分,不应该用于生产。我也没有得到评论所争论的关于有状态元编程的问题。REGISTER_TYPE宏所做的唯一一件事就是声明一个新的函数重载。这怎么能被认为是状态修改?

为了让这个技巧停止工作,应该对 C++ 选择重载函数的规则进行一些根本性的更改,效果是每个中型程序都会停止编译。我对么?

标签: c++metaprogrammingtemplate-meta-programmingtypelist

解决方案


假设您有两个标题:

// foo.h
#ifndef foo_h
#define foo_h
#include "register_type.h"    
REGISTER_TYPE(int)
#endif

// bar.h
#ifndef bar_h
#define bar_h
#include "register_type.h"    
REGISTER_TYPE(double)
#endif

那么这两个来源将有不同的定义GetTypes(std::declval<Index<MAXIMUM_SIZE>>())

// a.cpp
#include "foo.h"
#include "bar.h"

inline void test() {
    std::cout << std::boolalpha 
        << std::is_same_v<
            GET_REGISTERED_TYPES,
            std::tuple<int, double>       // true
    << std::endl;
}

// b.cpp
#include "bar.h"     // <--- different order of includes
#include "foo.h"

inline void test() {
    std::cout << std::boolalpha 
        << std::is_same_v<
            GET_REGISTERED_TYPES,
            std::tuple<int, double>       // false !!
    << std::endl;
}

推荐阅读