Pergunta

I've seen various incarnations of my question answered/replied too but I'm still having a hard time figuring out how to omit functions my compiler states are ambiguous.

I have a class that is designed to handle streams of data. I have overloaded = and += operators so that the stream class can consume data of other types by transposing it into the template type T.

template<typename T>
class TStream
{
private:

    typedef TBuffer<T> stream_buffer;
    stream_buffer mStream;

public:

    TStream();
    TStream(const TStream &rhs);
    TStream::~TStream();

    unsigned int Size() const;
    unsigned int ByteStreamLength() const;
    void     Clear();

  // some methods omitted for brevity

    const T&operator[](unsigned int idx);
    const T*operator[](unsigned int idx) const;

    TStream<T> &operator=(const TStream &rhs);
    TStream<T> &operator=(T const);
    TStream<T> &operator=(unsigned char);
    TStream<T> &operator=(bool);
    TStream<T> &operator=(double);

  // rest omitted for brevity
};

Doing this

TStream<unsigned char> ByteStream

causes ambiguity with

operator=(unsigned char).

I basically want operator=(unsigned char) to be omitted if T = unsigned char.

This article appears to give a way to do it: http://www.drdobbs.com/function-overloading-based-on-arbitrary/184401659

But having a hard time following it because I don't want to alter my return types.

I typically use TStream like this:

   TStream<unsigned byte> ByteStream;
   ByteStream+= int
   ByteStream+= char

... etc, where I overload the += in the same way. I deduce the size coming in and convert it to the T. I do this to avoid passing a void * and a length argument for the simple POD cases.

Foi útil?

Solução

There is an easy way, and a legal way.

The easy way is to use SFINAE on the function in question. Sadly, as this results in a function template for which there is no valid specialization, this is technically an ill formed program (no diagnostic required). So avoid that.

The legal way would be to use CRTP.

template<typename D, typename T, typename U>
struct implement_operator_plus_equals {
  implement_operator_plus_equals() {
    static_assert( std::is_base_of<implement_operator_plus_equals, D>::value, "CRTP failure" );
  }
  D* self() { return static_cast<D*>(this); }
  D const* self() const { return static_cast<D const*>(this); }

  typedef D Self;

  Self& operator+=( U u ) {
    static_assert( (sizeof(U)%sizeof(T)) == 0, "Non integer number of Ts in a U" );
    T* b = reinterpret_cast<T*>(&u);
    T* e = b + sizeof(U)/sizeof(T);
    for (T* it = b; it != e; ++it) {
      self()->consume(*it);
    return *self();
  }
};
template<typename D, typename T>
struct implement_operator_plus_equals<D,T,T> {
  implement_operator_plus_equals() {
    static_assert(
      std::is_base_of<implement_operator_plus_equals, D>::value, "CRTP failure"
    );
    // Have an operator+= that cannot be called, so using ...::operator+= is legal,
    // but mostly harmless:
    class block_call {};
    void operator+=( block_call ) {}
  }
};

Then use it:

template<typename D,typename T>
struct plus_equals_helper:
  public implement_operator_plus_equals< TStream<T>, T, int >,
  public implement_operator_plus_equals< TStream<T>, T, unsigned char >,
  public implement_operator_plus_equals< TStream<T>, T, bool >,
  public implement_operator_plus_equals< TStream<T>, T, double >
{
  // move += down from parents (to fix problem OP reported):
  using implement_operator_plus_equals< TStream<T>, T, int >::operator+=;
  using implement_operator_plus_equals< TStream<T>, T, unsigned char >::operator+=;
  using implement_operator_plus_equals< TStream<T>, T, bool >::operator+=;
  using implement_operator_plus_equals< TStream<T>, T, double >::operator+=;
};

template<typename T>
class TStream:
  public plus_equals_helper< TStream<T>, T >
{
public:
  void consume( T t ) { /* code */ }
  using plus_equals_helper<TStream<T>, T>::operator+=;
  TStream const& operator+=( T t ) { consume(t); return *this; }
};

this is legal, because the specializations that do nothing are perfectly ok.

On the other hand, the rule that all template functions must have at least one valid specialization is pretty obscure, and usually nothing really bad happens if you violate it. So you could just SFINAE disable your methods using a second unused default argument.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top