Я видел, что различные инкарнации моего вопроса отвечали/отвечали тоже, но мне все еще трудно понять, как опустить функции, которые мои состояния компилятора неоднозначны.
У меня есть класс, который предназначен для обработки потоков данных. Я перегрузил операторы = и + =, чтобы класс потока мог потреблять данные других типов, переставляя его в шаблон типа 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
};
Делая это
TStream<unsigned char> ByteStream
вызывает неоднозначность с
operator=(unsigned char).
Я просто хочу, чтобы оператор = (unsigned char) был опущен, если T = unsigned char.
Эта статья, как представляется, дает возможность сделать это: http://www.drdobbs.com/function-overloading-based-on-arbitrary/184401659
Но с этим трудно справиться, потому что я не хочу менять свои возвращаемые типы.
Обычно я использую TStream следующим образом:
TStream<unsigned byte> ByteStream;
ByteStream+= int
ByteStream+= char
... и т.д., где я перегружаю + = таким же образом. Я выводил размер входящего и преобразовывал его в T. Я делаю это, чтобы избежать передачи void * и аргумента длины для простых случаев POD.
Существует простой путь и законный путь.
Легкий способ - использовать SFINAE для рассматриваемой функции. К сожалению, поскольку это приводит к шаблону функции, для которого нет действительной специализации, это технически плохо сформированная программа (не требуется диагностика). Поэтому избегайте этого.
Юридическим путем было бы использование 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 ) {}
}
};
Затем используйте его:
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; }
};
это законно, потому что специализации, которые ничего не делают, совершенно в порядке.
С другой стороны, правило, что все функции шаблона должны иметь хотя бы одну действительную специализацию, довольно неясно, и обычно ничего плохого не происходит, если вы нарушаете его. Таким образом, вы можете просто отключить SFINAE свои методы, используя второй неиспользуемый аргумент по умолчанию.
template<typename T>
.
TStream::~ptiStream();