Я нахожусь в процессе работы над классом, и у меня есть 3 массива в моем классе, где все они представляют одни и те же данные, но в другом формате. У меня перегрузка <<
оператор, который объявлен за пределами моего класса, который использует const-ссылку на него, а не как друга этого класса.
SomeClass {
public:
// Nameless Union - All 3 Arrays Are of The Same Exact Data Type
// And All 3 Arrays Have The Same Exact Size. This Nameless Union
// Uses The Same Memory Address For All 3 Arrays And Their Elements.
// So An Element Is Changed By One Array Type, It Is Expected And
// Accepted For It To Change The Others. This Is Not 3 Different
// Arrays, This Is Still 1 Array Of Size 256, Just Different
// Representations Or Different Ways To Access Them.
union {
int m_256[256];
int m_16[16][16];
int m_4[4][4][4][4];
};
SomeClass() { std::fill( std::begin( m_256 ), std::end( m_256 ), 0 ); }
}; // SomeClass
std::ostream& operator<<( std::ostream& out, const SomeClass& c ) {
out << std::endl;
for ( unsigned box = 0; box < 4; box++ ) {
for ( unsigned slice = 0; slice < 4; slice++ ) {
for ( unsigned row = 0; row < 4; row++ ) {
for ( unsigned col = 0; col < 4; col++ ) {
out << "(" << box << "," << slice << "," << row << "," << col << ") = "<< c.m_4[box][slice][row][col] << std::endl;
}
}
}
}
return out;
} // operator<<
Это то, что у меня сейчас есть. То, что я хотел бы сделать, это также использовать operator<<
с этим классом, но чтобы иметь возможность различать способ отображения одних и тех же данных в другом формате.
Я знаю, что вы не можете сделать это: добавив 2-й
std::ostream& operator<<( std::ostream& out, const SomeClass& c ) {
out << std::endl;
for ( unsigned i = 0; i < 16; i++ ) {
for ( unsigned j = 0; j < 16; j++ ) {
out << "(" << i << "," << j << ") = " << c.m_16[i][j] << std::endl;
}
}
return out;
} // operator<<
И третий
std::ostream& operator<<( std::ostream& out, const SomeClass& c ) {
out << std::endl;
for ( unsigned u = 0; u < 256; u++ ) {
out << u << " = " << m_256[u] << std::endl;
}
return out;
} // operator<<
В связи с тем, что это неоднозначно. Тем не менее, я хотел бы иметь функциональность для отображения в любом из 3 различных форматов.
Есть ли какие-либо рабочие раунды или решения этой проблемы? Я хотел бы иметь возможность просто отправить объект класса оператору потока, и эти типы операторов не могут принимать дополнительные параметры, поскольку они являются бинарными операторами, а не функцией.
Вы можете просто использовать класс адаптера для записи вывода. Вы можете передать спецификатор формата в конструктор или дифференцировать по типу. Например (дифференциация по типу):
struct SomeClassAs256 {
SomeClass const& x_;
explicit(SomeClass const& x) : x_(x) {}
};
И тогда есть оператор<< реализация:
ostream& operator<<(ostream& os, SomeClassAs256 const& x) {
...
return os;
}
И тогда вы используете это:
SomeClass x;
...
cout << SomeClassAs256(x) << endl;
Вы не можете сделать это, по крайней мере, не так просто.
Это оставляет вам два варианта: использовать две функции, которые создают строку и возвращает ее, или создавать структуру потокового манипулятора.
Создать набор функций форматирования, который возвращает строку, которая затем используется для вывода, просто с помощью std::ostringstream
:
std::string format1(SomeClass const& c)
{
std::ostringstream os;
os << whatever you want ...
return os.str();
}
Создание структур манипуляторов немного сложнее, но также может быть более гибким и мощным:
class format1
{
public:
format1(SomeClass const& c)
: c_(c)
{}
friend std::ostream& operator<<(std::ostream& os,
format1 const& fmt)
{
os << some formated output here using `fmt.c_`...;
return os;
}
private:
SomeClass const& c_;
};
В обоих случаях вы можете использовать его одинаково:
SomeClass c(...);
std::cout << format1(c) << '\n';
Вы можете сделать это, возвращая прокси (для обеспечения адаптации) из вашего класса, используя make-функции и используя:
оператор << (ostream, SomeClass :: Proxy) в качестве оператора вывода. Я поработаю над примером кода.
Таким образом, вам не нужно выставлять внутренности вашего класса тоже. Ничего плохого в том, чтобы сделать оператора << друзья …
Пример:
#include <iostream>
class SomeClass {
union {
int m_256[256];
int m_16[16][16];
int m_4[4][4][4][4];
};
public:
SomeClass() { std::fill( std::begin( m_256 ), std::end( m_256 ), 0 ); }
struct x256
{
const SomeClass& c_;
explicit x256(const SomeClass& c): c_(c)
{
}
};
struct x16
{
const SomeClass& c_;
explicit x16(const SomeClass& c): c_(c)
{
}
};
struct x4
{
const SomeClass& c_;
explicit x4(const SomeClass& c): c_(c)
{
}
};
x256 output265() const
{
return x256(*this);
}
x16 output16() const
{
return x16(*this);
}
x4 output4() const
{
return x4(*this);
}
friend std::ostream& operator<<( std::ostream& out, const SomeClass::x256& c ) {
out << std::endl;
for ( unsigned u = 0; u < 256; u++ ) {
out << u << " = " << c.c_.m_256[u] << std::endl;
}
return out;
} // operator<<
friend std::ostream& operator<<( std::ostream& out, const SomeClass::x16& c ) {
//...
return out;
} // operator<<
friend std::ostream& operator<<( std::ostream& out, const SomeClass::x4& c ) {
//...
return out;
} // operator<<
}; // SomeClass
void testSomeClass()
{
SomeClass someClass;
std::cout << someClass.output265() << someClass.output16() << someClass.output4() << std::endl;
}
Увидев несколько хороших ответов и учитывая, что ostream
объект и operator<<
не может знать, какой тип использовать, и, полагая, что пользователь решит отобразить информацию для своих нужд, я пошел другим путем; однако решение, которое я нашел, которое работает для моих текущих потребностей, было поддержано и вдохновлено всеми теми, кто оставил отличные ответы на эту проблему.
Направление, которое я выбрал, было таким; Я добавил enum
в мой класс непосредственно с 3 типами. Я добавил публичную функцию, которая выводит строку и принимает enum
введите в качестве параметра. Я добавил ostream operator<<
в мой класс и параметр, который он принимает, является typename
на мои занятия enum
, Я использую функцию out, чтобы разветвлять мои 3 различных способа отображения информации. Так что теперь в другом разделе кода, который использует этот объект, я могу передать экземпляр, вызывающий функцию out, которая возвращает строку, передавая требуемый тип. Мой класс теперь выглядит так:
class SomeClass {
public:
enum OutputType { x256, x16, x4 };
union {
int m_256[256];
int m_16[16][16];
int m_4[4][4][4][4];
};
std::string out( OutputType type ) const;
std::ostream& operator<<( typename SomeClass::OutputType type );
}; // SomeClassstd::ostream& SomeClass::operator<<( typename SomeClass::OutputType type ) {
return std::ostream << out(type );
} // operator<<
std::string SomeClass::out( OutputType type ) const {
std::ostringstream out;
out << std::endl;
switch( type ) {
case: x256: {
// Print Format Here
break;
}
case x16: {
// Print Format Here
break;
}
case x4: {
// Print Format Here
break;
}
default: {
// Error Message
return out.str();
}
}
return out.str();
} // out
Я не знаю, связано ли это с моим классом в моем проекте, будучи template
или как operator<<
реализовано, но я должен был использовать typename
с в объявлении / определении функции для его работы, так что это выглядит в моем коде, за исключением имени класса.
template< class T>
std::ostringstream& SomeClass<T>::operator<<( typename SomeClass<T>::Type type ) {
// Code Here
}
Я ценю всю помощь и предложения, которые вы все предложили, и я принимаю тот совет, который был дан всем сердцем, спасибо всем.
редактировать
Теперь, если я хочу сделать это немного проще для пользователя: я мог бы переместить свою функцию out в приватный раздел; Напишите 3 обертки или напечатайте функции, которые не принимают никаких параметров, но они устанавливают переменную в закрытый метод.
Добавьте некоторый способ, например, член к вашему классу, чтобы решить выходной формат:
public:
enum OutputStyle
{
M_256,
M_16,
M_4,
};
OutputStyle style() const {return style_;}
private:
mutable OutputStyle style_ = M_256;
Добавьте некоторый способ, например, оператор вызова функции, чтобы установить это:
public:
SomeClass const& operator()(OutputStyle s) const
{
style_ = s;
return *this;
}
Сделать << оператор, чтобы рассмотреть это:
std::ostream& operator<<( std::ostream& out, const SomeClass& c )
{
switch( c.style() )
{
default:
assert(!"defective operator <<");
case SomeClass::M_256:
// ... output like M_256 here
break;
case SomeClass::M_16:
// ... output like M_16 here
break;
case SomeClass::M_4:
// ... output like M_4 here
break;
}
}
Затем вы можете изменить его до или во время вывода:
SomeClass x; // <- has style M_256
x(SomeClass::M_16);
std::cout << "current:" << x << std::endl
<< "with M_4:" << x(SomeClass::M_4) << std::endl;