C++03 中缺少 typeof 运算符?

2021-12-24 00:00:00 templates metaprogramming c++ boost typeof

我只是想知道 boost 是如何实现的BOOST_TYPEOF(在 C++03 中)这似乎是一个非常有用的工具.有人知道吗?

I'm just wondering how boost have implemented BOOST_TYPEOF (in C++03) which seems to be a very useful tool. Anyone has any idea?

另外,我认为 C++03 本身可以提供 typeof 运算符,尤其是当它已经具有 sizeof(expr) 时,必须strong> 还要知道 expr 的 type,否则它怎么能告诉我们 大小,而不知道 type?真的有可能知道大小,而不知道一个表达式的类型吗?

Also, I'm thinking C++03 itself could have provided typeof operator, especially when it already has sizeof(expr) which must be knowing the type of the expr also, otherwise how else could it tell us the size, without knowing the type?Is it really possible to know the size, without knowing the type of an expression?

如果它不知道type,那么编译器会告诉我们size是什么(如果不是type)?我的意思是,sizeof(unknowntype) 对编译器(以及人类)没有意义!

If it doesn't know the type, then compiler tells us the size of what (if not type)? I mean, sizeof(unknowntype) wouldn't make sense to the compilers (and humans as well)!

推荐答案

它只是使用了编译器魔法.比如 GCC 的 __typeof__.对于不提供这种魔法的编译器,它提供了一种可以检测some 表达式类型的模拟,但在完全未知类型的情况下会失败.

It just uses compiler magics. Like, GCC's __typeof__. For compilers that don't provide such magic, it provides an emulation that can detect the type of some expressions, but fails with completely unknown types.

一个可能的实现可能是拥有一个接受给定类型表达式的函数列表,然后使用类模板从该类型分派到一个数字.为了让函数模板返回数字作为编译时实体,我们将其放入数组维度

A possible implementation could be to have a list of functions that accept an expression of a given type, and then dispatch from that type to a number using a class template. To make the function template return the number as a compile time entity, we put it into an array dimension

template<typename> struct type2num;
template<int> struct num2type;
template<typename T> typename type2num<T>::dim &dispatch(T const&);

然后它从那个数字返回到类型,这样我们的 EMUL_TYPEOF 可以直接命名类型.所以要注册一个类型,我们写

Then it goes from that number back to the type, so that our EMUL_TYPEOF could directly name the type. So to register a type, we write

#define REGISTER_TYPE(T, N) 
  template<> 
  struct type2num<T> { 
    static int const value = N; 
    typedef char dim[N]; 
  }; 
  template<> 
  struct num2type<N> { typedef T type; }

有了这个,你就可以写

#define EMUL_TYPEOF(E) 
  num2type<sizeof dispatch(E)>::type

每当你需要注册一个类型时,你就写

Whenever you need to register a type, you write

REGISTER_TYPE(int, 1);
REGISTER_TYPE(unsigned int, 2);
// ...

当然,现在您发现您需要一种机制来接受vector,而您事先并不知道T,然后它会变得任意复杂.您可以创建一个系统,其中数字的含义不仅仅是一种类型.这可能有效:

Of course, now you find you need a mechanism to accept vector<T>, where you don't know T in advance and then it gets arbitrary complex. You could create a system where the numbers mean more than just a type. This could probably work:

#define EMUL_TYPEOF(E) 
  build_type<sizeof dispatch_1(E), sizeof dispatch_2(E)>::type

这可以检测像 int 这样的类型,也可以检测像 shared_ptr<int> 这样的类型――换句话说,不是类模板特化的类型,以及具有一个模板参数,通过做某种系统映射

This could detect types like int and also types like shared_ptr<int> - in other words, types that aren't class template specializations, and class template specializations with one template argument, by doing some kind of systematical mapping

  • 如果第一个数字产生 1,则第二个数字指定一个类型;否则
  • 第一个数字指定一个模板,第二个数字是第一个类型模板参数

这样就变成了

template<int N, int M>
struct build_type {
  typedef typename num2tmp<N>::template apply<
    typename num2type<M>::type>::type type;
};

template<int N>
struct build_type<1, N> {
  typedef num2type<N>::type type;
};

我们还需要更改dispatch模板并将其拆分为两个版本,如下所示,以及用于注册单参数模板的REGISTER_TEMP1

We also need to change the dispatch template and split it up in two versions, shown below, alongside the REGISTER_TEMP1 for registering one-argument templates

template<typename T> typename type2num<T>::dim1 &dispatch_1(T const&);
template<typename T> typename type2num<T>::dim2 &dispatch_2(T const&);

#define REGISTER_TYPE(T, N) 
  template<> 
  struct type2num<T> { 
    static int const value_dim1 = 1; 
    static int const value_dim2 = N; 
    typedef char dim1[value_dim1]; 
    typedef char dim2[value_dim2]; 
  }; 
  template<> 
  struct num2type<N> { typedef T type; }

#define REGISTER_TMP1(Te, N) 
  template<typename T1> 
  struct type2num< Te<T1> > { 
    static int const value_dim1 = N; 
    static int const value_dim2 = type2num<T1>::value_dim2; 
    typedef char dim1[value_dim1]; 
    typedef char dim2[value_dim2]; 
  }; 
  template<> struct num2tmp<N> { 
    template<typename T1> struct apply { 
      typedef Te<T1> type; 
    }; 
  }

注册 std::vector 模板和两个 int 变体现在看起来像

Registering the std::vector template and both int variants now look like

REGISTER_TMP1(std::vector, 2);
// ... REGISTER_TMP1(std::list, 3);

REGISTER_TYPE(int, 1);
REGISTER_TYPE(unsigned int, 2);
// ... REGISTER_TYPE(char, 3);

您可能还想为每种类型注册多个数字,为??每个 const/volatile 组合注册一个数字,或者可能需要每个类型多个数字来记录 *& 之类的.您还想支持 vector<;向量>,因此模板参数也需要多个数字,从而使 build_type 递归调用自身.由于您可以创建任意长的整数列表,因此无论如何您都可以将任何内容编码到该序列中,因此如何表示这些内容完全取决于您的创造力.

You probably also want to register multiple numbers with each type, one number for each combination of const/volatile or may need more than one number per type for recording *, & and such. You also want to support vector< vector<int> >, so you need more than one number for the template argument too, making build_type call itself recursively. As you can create an arbitrary long list of integers, you can encode anything into that sequence anyway, so it's just up to your creativity on how to represent these things.

最后,您可能正在重新实现 BOOST_TYPEOF :)

In the end, you are probably reimplementing BOOST_TYPEOF :)

相关文章