如何强制c ++类方法只接受少数整数文字?

时间:2011-09-22 16:18:38

标签: c++ templates standards

有一个我正在重构的课程,目前有一个方法:

void resize(size_t sz)

在当前的代码库中,sz始终为0,1,2或3。 底层类正在从动态分配更改为maxsize == 3的预分配数组。

如果有人试图调整为sz> 3,怎么会出现构建时错误? 添加运行时检查很容易。但我宁愿让编译时检查更快失败。

我不想更改任何使用入站的整数文字进行调用的现有代码,例如:

x.resize(2)

仍应按原样编译。

但如果有人出现并试图

x.resize(4)
  or
x.resize(n)

它应该无法编译或链接。

我在想一个专门针对int的模板,除了{0,1,2,3}之外的任何其他内容都是未定义的。但我不确定如何在标准c ++的范围内做到我想做的事。

编辑:

我应该详细说明我使用模板的想法。我非常愿意更改resize函数的声明。我不愿意更改主叫代码。

e.g。我在想像

void resize( ConstructedFromLiteral<0,3> sz)

void resize( ConstructedFromLiteral<0> sz)
void resize( ConstructedFromLiteral<1> sz)
void resize( ConstructedFromLiteral<2> sz)
void resize( ConstructedFromLiteral<3> sz)

5 个答案:

答案 0 :(得分:6)

您无法通过编译时检查运行时值。想象一下,如果你说,

resize(read_some_number_from_disk());

编译器应如何检查?

但是,可以使该函数成为模板,因为模板参数在编译时已知

class Foo
{
  template <unsigned int N> void resize()
  {
    static_assert(N < 4, "Error!");
    //...
  }

  //...

};

如果你没有静态断言,你可以装配你自己的静态断言类,它将无法编译:

template <bool> struct ecstatic_assert;  // no definition!
template <> struct ecstatic_assert<true> { } ;

用法:

... resize ... { ecstatic_assert<N < 4> ignore_me; /* ... */ } 

答案 1 :(得分:3)

我在编译时使用static_assert来检查:

struct foo {
  template <int N> 
  void resize() {
    static_assert(N >= 0 && N < 4);
  }
};

你在C ++ 11中内置了static_assert,但它也是easy enough to implement in C++03。与专业化相比,它可以避免一些繁琐的代码重复。

答案 2 :(得分:1)

你不能这样做。您不能保持函数调用按原样调整大小,并在编译时检查n,因为它是运行时值。您将需要重构代码以获得编译时错误(例如std / boost static_assert)。

答案 3 :(得分:0)

您可以为整数{0,1,2,3}创建四个公共内联专用模板。这些是简单的单行函数,可以为任何int调用私有的普通函数。

编辑:对于那些说使用专门模板无法完成的反对者(为什么是downvotes?):

class Demo {
private:
    template<int MyInt>
    void PrivateFunction() {
        cout << MyInt << endl;
    }
public:
    template<int MyInt> void PublicFunction();
    template<> void PublicFunction<0>() { PrivateFunction<0>(); }
    template<> void PublicFunction<1>() { PrivateFunction<1>(); }
    template<> void PublicFunction<2>() { PrivateFunction<2>(); }
    template<> void PublicFunction<3>() { PrivateFunction<3>(); }
};

尝试调用Demo :: PublicFunction&lt; 4&gt;(),您将收到链接器错误。如果您没有/不想创建static_assert,那么完成同样的事情并且很有用。

正如其他人所提到的,检查参数的值不是那么容易......

答案 4 :(得分:-1)

出于明显的原因,我不喜欢这个答案,但它确实满足了您的要求:

struct x {
  void true_resize(int n) { }
  template <int N> void template_resize();
};
  template<> void x::template_resize<0>() { true_resize(0); }
  template<> void x::template_resize<1>() { true_resize(1); }
  template<> void x::template_resize<2>() { true_resize(2); }
  template<> void x::template_resize<3>() { true_resize(3); }
#define resize(x) template_resize<x>();


int main () {
  x x;
  x.resize(2);
  x.resize(4);
}

如果不明显,#define不遵守C ++范围规则,因此将名称resize用于所有其他用途。