I have a class Channel with two two properties, direction and size which are fixed during construction. Direction can take only one of two values, forward (1) or backward(-1). Size can take any value, but there is a physically meaningful distinction between 0 and any nonzero value.
I'd like to be able to write functions that accept Channel objects with known values for the direction and/or size, and I thought to implement this using derived classes:
Channel
|
-----------------------------------------------
| | | |
ForwardChannel BackwardChannel ZeroChannel NonzeroChannel
| | | |
| ---------------- ...
| | |
| BackwardZeroChannel |
| |
---------------------------------
|
ForwardZeroChannel
Obviously I didn't draw all of the permutations.
I tried implementing it as so
class Channel {
Channel(int direction, int size) { ... };
...
}
class ForwardChannel: public virtual Channel {
ForwardChannel(int size) : Channel(1, size) { ... }
...
}
class ZeroChannel: public virtual Channel {
ZeroChannel(int direction) : Channel(direction, 0) { ... }
...
}
class ForwardZeroChannel: public ForwardChannel, ZeroChannel {
ForwardZeroChannel() : ForwardChannel(0), ZeroChannel(1)
...
}
Instantiating ForwardChannel and ZeroChannel works fine. Instantiating ForwardZeroChannel calls only the default constructor for Channel which doesn't set the values. I have to add Channel(1, 0) to the initializer list:
class ForwardZeroChannel: public ForwardChannel, ZeroChannel {
ForwardZeroChannel() : Channel(0, 1), ForwardChannel(0), ZeroChannel(1)
...
}
but that seems to defeat some of the purpose of deriving from ForwardChannel and ZeroChannel. Is there a better way of doing this?
what about (following need c++11, but it could be ported to c++99 (except the 'template using') ):
class Channel {
public:
virtual ~Channel();
protected:
Channel(int direction, int size);
};
template<bool forward, bool zero>
class ChannelT : public Channel {
public:
template <bool b = zero, typename T = typename std::enable_if<b>::type>
ChannelT() : Channel(forward ? 1 : 0, 0) {}
template <bool b = zero, typename T = typename std::enable_if<!b>::type>
explicit ChannelT(int size) : Channel(forward ? 1 : 0, size) { assert(size != 0); }
};
template <bool zero> using ForwardChannel = ChannelT<true, zero>;
using ForwardZeroChannel = ChannelT<true, true>;
using ForwardNonZeroChannel = ChannelT<true, false>;
// And so on for the 5 other types...
int main() {
ForwardZeroChannel forwardZeroChannel;
ForwardNonZeroChannel forwardNonZeroChannel(42);
return 0;
}