我可以在编译时强制执行此操作员调用计数要求吗?

时间:2013-07-23 10:54:08

标签: c++ assertions

我有一个类,接口方面,就像这样简单:

struct Foo
{
    inline Foo & operator << (int i)
    {
        return *this;
    }
};

然后我可以通过以下方式使用它:

Foo foo;
foo << 1 << 2 << 3 << 4;

现在我想限制此运算符的用法。 例如,我希望它在序列点之间被称为偶数次。

我目前使用内部代理类解决此问题。创建一个临时值,它在控制序列的末尾被销毁并检查操作符被调用的次数:

struct Foo
{
    inline Foo() : m_count(0) {}

private:
    struct FooProxy
    {
        friend struct Foo;

        inline ~FooProxy();
        inline struct Foo & operator << (int i);

    private:
        inline FooProxy(struct Foo &foo) : m_foo(foo) {}
        struct Foo &m_foo;
    };

public:
    inline FooProxy operator << (int i);

private:
    int m_count;
};

inline Foo::FooProxy Foo::operator << (int i)
{
    ++m_count;
    return FooProxy(*this);
}

inline Foo & Foo::FooProxy::operator << (int i)
{
    ++m_foo.m_count;
    return m_foo;
}

inline Foo::FooProxy::~FooProxy()
{
    assert(m_foo.m_count % 2 == 0);
}

有一些警告,但它主要完成这项工作:

Foo foo;
foo << 1 << 2 << 3 << 4; /* is OK */
foo << 1 << 2 << 3; /* triggers an assert */

现在我想知道是否有一种方法可以在编译时强制执行此操作,使用相同的代理技术或使用其他策略。

我希望实现的另一个示例:在将任意数量的int传递给运营商之后,强制执行至少一个float

foo << 1 << 2 << 3.f << 4.f << 5; /* is OK */
foo << 1 << 2 << 3.f << 4.f; /* illegal because one `int` is needed */

2 个答案:

答案 0 :(得分:2)

为什么不使用FooPair之类的东西来强制执行偶数:

struct FooPair
{
  int m_x, m_y;

  FooPair(int x, int) : m_x(x), m_y(y)
  {
  }
};

inline Foo & operator << (const FooPair &pair)
{
  return *this;
}

所以人们不得不称之为:

Foo foo;
foo << FooPair(1,2) << FooPair(3,4);

它更详细,但会确保传递偶数个值。

答案 1 :(得分:1)

您可以使用模板代理将状态编码为模板参数而不是成员。

但是,除非您使用某些条件的最终返回值,否则您只能检查某些条件,而不能检查其他条件。例如,你可以检查一个int是在float之前插入的,还是没有在一行中插入两个浮点数,但你不能检查在任何浮点数之后是否插入了一个int。

通常,您可以通过简单地将插入运算符专门化为无效状态的无效内容来检测下一次插入之前必须满足的任何条件。但你无法检查最终状态,因为所有代理必须是可破坏的(每个代理都是不同的,因此所有中间代理都将被销毁)。