2013-07-23 2 views
4

У меня есть класс, который, интерфейс-накрест, так просто, как это:Могу ли я выполнить требование счетчика вызовов оператора во время компиляции?

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 */ 
+0

Просьба указать предполагаемый прецедент, поскольку в нем нет причин, по которым ответ Шона не будет работать. –

+0

Готовы ли вы пожертвовать синтаксисом оператора для вызова функции? – jrok

+0

Вам нужно поддерживать C++ 03? Если вы можете использовать C++ 11, вам следует использовать шаблоны вариационной функции. –

ответ

1

Вы можете использовать прокси-сервер шаблона, который будет кодировать состояние как параметр шаблона, а не член.

Однако, если вы не используете возможное возвращаемое значение для чего-то, вы можете только проверить некоторые условия, но не другие. Например, вы можете проверить, что int был вставлен перед float или что в строку не вставлено два поплавка, но вы не можете проверить, вставлен ли int после любых поплавков.

Как правило, вы можете обнаружить любое условие, которое должно быть выполнено перед следующей вставкой, просто выделив оператор вставки для чего-то недействительного для недопустимых состояний. Но вы не можете проверить конечное состояние, потому что все прокси должны быть разрушаемыми (каждый из них отличается, поэтому все промежуточные будут уничтожены).

+0

Это именно то, что я сейчас делаю, и я надеялся, что есть трюк, который я мог бы упустить. Я принимаю этот ответ, потому что в этом конкретном случае «вы не можете этого сделать» для меня более полезно, чем «сделать это по-другому». Благодаря! –

2

Почему бы не использовать что-то вроде FooPair для обеспечения четная -ness:

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); 

Это более многословным, но будет гарантировать, что четное число значений передаются.

+2

Зачем изобретать 'std :: pair'? – nijansen

+0

@Sean Потому что это не проблема, которую я хочу решить. Я добавлю другие примеры. –