2

I'm working on some library code, and I want users to be able to take advantage of static binding if they are able to. If they are unable to instantiate a class at compile time, I want there to be a dynamic version of the class, so that it can be instantiated at run-time.

For a quick example, say I have a structure template A:

template<bool dynamic, int value=0> struct A
{
    static const int Value = value;
};


template<> struct A<true>
{
    int Value;
    A(int value) : Value(value) {}
};

These definitions allows users of the library to instantiate A statically, and dynamically:

A<true> dynamicA = A<true>(5);

A<false, 5> staticA;

The problem with this method is that I have to write the definition of the class twice. I can think of a few ways of implementing a template that would generate both versions myself, but I can see it becoming a lot of work. Especially for classes that would use varying numbers of parameters, for example:

// It would be much harder to generate a static version of this class, 
// though it is possible with type lists.  Also, the way I'm imagining it, 
// the resulting classes probably wouldn't be very easy to use.
struct A
{
   vector<int> Values;
    A(vector<int> value) : Values(value) {}
};

Is there a name for this pattern / problem? Is there a meta programming library which has templates which can generate both definitions for me? How do I avoid having to write the definitions of my classes twice?

4

1 回答 1

2

有一种简单的机制可以将不依赖于动态/静态值问题的部分放到一个位置:将它们放入另一个类中,我们称之为它basic_A,然后调用您在问题中显示的静态/动态值容器value_A。有不同的方法可以连接value_Abasic_A形成完整的A类:

  1. basic_A里面的聚合value_A。这意味着您必须路由每种方法,basic_Avalue_Avalue_A. 这可能没什么好处,因为你必须复制所有的单行,所以从头开始。

  2. value_A里面的聚合basic_A。您要么也必须制作basic_A一个模板,只需将参数传递给value_A并为两种专业化提供正确的构造函数,可能以某种方式通过 SFINAE 禁用和启用它们。也不是一段非常漂亮且可维护的代码。另一种方法是为 的两个特化创建一个公共基类(接口),在该接口中value_A有一个,并将准备好的构造传递给的构造函数,代价是随时调用虚函数和 poitner 间接访问值。哎呀,特别是如果它是一个小而快速的轻量级类。unique_ptrbasic_Avalue_Abasic_AA

  3. 继承basic_Avalue_A。与 2. 中相同的问题适用于构造函数和模板参数的转发。

  4. 继承value_Abasic_A。构建问题消失了,但现在basic_A不能轻易访问value_A's 值。一种解决方案是拥有一个纯虚函数getValue()basic_A其中两个专业化value_A必须实现。这再次具有虚函数调度的成本,这对于小型轻量级类可能不理想,但它支持封装,因为basic_A它是非模板并且可以将其实现隐藏在 .cpp 文件中。另一种方法是通过 CRTP 使用编译时多态性,这将basic_A再次制作模板。

以下是 4. 的两种方法的两个示例:

4a: getValue()作为虚函数:

//basic_a.hpp

struct basic_A {
  int foo() const;
  virtual int getValue() const = 0;
};

//basic_A.cpp
int basic_A::foo() const { return 10 * getValue(); }

4b: getValue()通过 CRTP

template <class Value_t>
struct basic_A {
  int foo() const { return 10 * value_(); }
private:
  int value_() const { return static_cast<Value_t const&>(*this).getValue(); }
};

模板A又名。A_value对于 4b 如下。对于 4a,它几乎相同,只是丢失了模板参数和括号,basic_A因为它是一个普通类:

template <bool dyn, int value = 0> 
struct A;

template <>
struct A<true, 0> : basic_A<A<true, 0>>
{
  int val;
  int getValue() const { return val; }
};

template <int value>
struct A<false, value> : basic_A<A<false,value>>
{
  int geValue() { return value; }
};
于 2014-11-12T14:47:56.880 回答