简体   繁体   English

C ++重载I / 0运算符:克服歧义

[英]C++ Overloading the I/0 operators: Getting Past Ambiguity

I'm in the process of working on a class, and I have 3 arrays in my class where they all represent the same data but in a different format. 我正在研究一个类,并且在我的类中有3个数组,它们全部代表相同的数据,但格式不同。 I have overload the << operator that is declared outside of my class that takes a const refererence to it and not as a friend of this class. 我重载了在类之外声明的<<运算符,该运算符采用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<<

This is what I currently have. 这就是我目前所拥有的。 What I would like to be able to do is to also use the operator<< with this class as well, but to be able to distinguish a way to display the same data in a different format. 我想做的是也将operator<<与此类一起使用,但是要能够区分一种以不同格式显示相同数据的方式。

I know that you can not do this: by adding a 2nd 我知道您不能这样做:添加第二个

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

And a 3rd 还有第三

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

Due to the fact that this is ambiguous. 由于这是模棱两可的事实。 Yet I would like to have the functionality to display it in any of the 3 different formats. 但是我想具有以3种不同格式显示它的功能。

Are there any work a rounds or solutions to this problem? 是否有任何工作回合或解决此问题的方法? I'd like to be able to just send the class object to the stream operator, and these types of operators can not accept additional parameters since they are binary operators and not a function. 我希望能够仅将类对象发送到流运算符,并且这些类型的运算符不能接受其他参数,因为它们是二进制运算符而不是函数。

You could just use an adaptor class to write the output. 您可以只使用适配器类来编写输出。 You could pass a format specifier into the constructor or differentiate by type. 您可以将格式说明符传递给构造函数或按类型区分。 For example (differentiating by type): 例如(按类型区分):

struct SomeClassAs256 {
  SomeClass const& x_;

  explicit(SomeClass const& x) : x_(x) {}
};

And then have an operator<< implementation: 然后有一个operator <<实现:

ostream& operator<<(ostream& os, SomeClassAs256 const& x) {
  ...
  return os;
}

And then you use it: 然后使用它:

SomeClass x;
...
cout << SomeClassAs256(x) << endl;

You can't really do that, at least not that simply. 您不能真正做到这一点,至少不能那么简单。

That leaves you with two choices: Use two functions which creates a string and returns it, or create a stream manipulator structure. 剩下两个选择:使用两个函数创建一个字符串并返回它,或者创建一个流操纵器结构。

Creating a set of formatting functions which returns a string that is then use for the output is simple using std::ostringstream : 使用std::ostringstream创建一组格式化函数以返回一个字符串,然后将该字符串用于输出非常简单:

std::string format1(SomeClass const& c)
{
    std::ostringstream os;
    os << whatever you want ...
    return os.str();
}

Creating manipulator structures is a little more complicated, but can also be more flexible and powerful: 创建操纵器结构稍微复杂一些,但也可以更加灵活和强大:

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_;
};

In both cases you can use it the same way: 在两种情况下,您都可以以相同的方式使用它:

SomeClass c(...);
std::cout << format1(c) << '\n';

You can do this by returning a proxy (to provide the adaption) from your class using make functions and using: operator << (ostream, SomeClass::Proxy) as output operator. 您可以通过使用make函数并使用:运算符<<(ostream,SomeClass :: Proxy)作为输出运算符从类中返回一个代理(以提供适应功能)来实现此目的。 I'll work on some example code. 我将处理一些示例代码。

This way you don't need to expose the internals of your class too. 这样,您也无需公开类的内部结构。 Nothing wrong with making operator << friends... 让操作员<<朋友没错...

Example: 例:

#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;
}

Add some way for example member to your class to decide output format: 在类中添加一些示例例如member来确定输出格式:

public:
    enum OutputStyle 
    {
        M_256,
        M_16,
        M_4,
    };
    OutputStyle style() const {return style_;}

 private:  
     mutable OutputStyle style_ = M_256;

Add some way for example function call operator to set that: 添加一些方法,例如函数调用运算符来进行设置:

public:
    SomeClass const& operator()(OutputStyle s) const
    {
        style_ = s;
        return *this;
    }

Make the << operator to consider it: 使<<操作符进行考虑:

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

Then you can change it before or during output: 然后,您可以在输出之前或期间进行更改:

    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;

After seeing some great answers and taking into the consideration that the ostream object and operator<< can not know which type to use and figuring that it would be decided by the user to display the information to their needs, I went a different route; 在看到一些很好的答案并考虑到ostream对象和operator<<无法知道要使用哪种类型,并确定用户将根据自己的需要显示信息后,我选择了另一条路线。 however the solution that I have come up with that works for my current needs was aided and inspired by all of those who have left great answers to this problem. 但是,我提出了可以满足我当前需求的解决方案,并且得到了所有对此问题的很好回答的人的帮助和启发。

The direction that I took was this; 我所采取的方向是这样。 I added an enum to my class directly with the 3 types. 我直接用3种类型向我们的班级添加了一个enum I added a public function that outputs a string and takes the enum type as a parameter. 我添加了一个公共函数,该函数输出一个字符串,并将enum类型作为参数。 I added the ostream operator<< to my class and the parameter that it takes is a typename to my classes enum . 我加入了ostream operator<<我的课,它需要的参数是typename ,以我的课enum I use the out function to branch my 3 different ways to display the information. 我使用out函数来分支3种不同的方式来显示信息。 So now in a different section of code that uses this object, I can pass the instance calling the out function that returns a string by passing in the type wanted. 因此,现在在使用该对象的代码的不同部分中,我可以传递调用out函数的实例,该函数通过传入所需的类型来返回字符串。 My class now looks like this: 我的班级现在看起来像这样:

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

}; // SomeClass


std::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

I don't know if it has to do with my class in my project being a template or the way the operator<< is implemented but I had to use typename with in the function declaration / definition for it to work, so this is what it looks like in my code except for the name of the class. 我不知道它是否与我项目中的类是template还是与operator<<的实现方式有关,但我必须在函数声明/定义中使用typename才能起作用,所以这就是除了类的名称外,它看起来像我的代码。

template< class T>
std::ostringstream& SomeClass<T>::operator<<( typename SomeClass<T>::Type type ) { 
    // Code Here
}

I appreciate all the help and suggestions that all of you have offered and I take that advice that was given to heart, thank you all. 我感谢你们所有人提供的所有帮助和建议,并且谨记那些给予您的建议,谢谢大家。

Edit 编辑

Now if I wanted to make this a little easier on the user: I could move my out function to the private section; 现在,如果我想让用户更轻松一些:我可以将out功能移到private部分; Write 3 wrapper out or print functions that doesn't take any parameters, but they set the variable to the private method. 编写3个不带任何参数的包装器或打印函数,但它们将变量设置为private方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM