如何为运营商模板<<对于ostream

时间:2010-10-27 22:25:50

标签: c++ templates

以下内容不会为我编译。我没有想法......有什么帮助吗?

template<>
inline
std::ostream& operator<< <const std::map<std::string, std::string> > (std::ostream& stream, const std::map<std::string, std::string>& some_map)
{
  return stream;
}

g ++给了我以下错误:

错误:'&lt;'之前的预期初始值设定项令牌

修改:1 好的,既然每个人都在告诉我超载,那么让我给你一个对重载没有意义的例子。如果我有这个怎么办:

template <typename T>
inline
std::ostream& operator<<(std::ostream& stream, const T& something)
{
  stream << something.toString();
  return stream;
}

class Foo
{
public:
  Foo(std::string s)
  {
    name = s;
  }

  std::string toString() const 
  {
    return name;
  }

private:
  std::string name;
};

class Bar
{
public:
  Bar(int i)
  {
    val = i;
  }

  std::string toString() const 
  {
    std::ostringstream stream;
    stream << val;
    return stream.str();
  }

private:
  int val;
};

int main(int, char**)
{
  Foo foo("hey");
  Bar bar(2);

  std::cout << foo << std::endl;
  std::cout << bar << std::endl;
  return 0;
}

现在这也行不通。

我只想避免重载运算符&lt;&lt;一遍又一遍地使用上面的模板。这似乎应该是可能的。我想知道它是否,如果是,如何?

在这种情况下,为了做同样的事情,Foo和Bar的重载都是浪费,这就是我试图避免它的原因。

编辑:2 好吧,似乎我被误解了。这是另一个澄清的尝试:

template <typename T>
std::ostream& operator<<(ostream& stream, const T& t)
{
  for(typename T::const_iterator i = t.begin(), end = t.end(); i != end; ++i)
  {
    stream << *i;
  }
  return stream;
}

int main(int, char**) 
{
  set<int> foo;
  list<string> bar;
  vector<double> baz;

  cout << foo << " " bar << " " << baz << endl;
};

以上代码不会让您高兴。抱怨模棱两可。但它似乎是打印出容器的更好解决方案。如果我做了重载,我需要写一个版本的运算符&lt;&lt;对于每个容器/数据类型组合,这将产生大量的代码重复。

3 个答案:

答案 0 :(得分:5)

这不需要是模板功能。

std::ostream & operator<<(std::ostream & stream, const std::map<std::string, std::string> & some_map)
{
    return stream;
}

修改

参考我关于用C ++编写Java的评论(对不起,如果它听起来粗鲁,我不打算成为smarmy)。告诉我这是否对你有效。而不是首先编写“toString”方法,只是重载运算符&lt;&lt;首先。功能几乎相同。然后,您可以编写一个非成员模板toString函数,该函数将自动与您的所有类一起使用,如下所示:

#include <sstream>
#include <string>

template<typename T>
std::string toString(const T & val)
{
    std::ostringstream ostr;
    ostr << val;
    return ostr.str();
}

修改2

如果您仍坚持按照自己的方式行事,那么这是我的另类选择。使用toString方法使所有类继承自带有虚拟toString方法的抽象类,然后编写一个运算符&lt;&lt;处理所有这些。

class Stringifiable
{
public:
    virtual std::string toString() const = 0;
};

std::ostream & operator<<(std::ostream & ostr, const Stringifiable& something)
{
    return ostr << something.toString();
}

现在编译器将选择您对模板的重载。

答案 1 :(得分:3)

您可以使用SFINAE来消除模板过载。请注意,这必须是函数的签名的一部分。因此,您可以使用boost :: enable_if:

template < typename T >
typename boost::enable_if< meta_function_to_check_for_concept<T>, std::ostream&>::type
operator << (std::ostream & out, T const& t)
{
  ...
}

如果你不这样做,那么你的模板将尝试匹配几乎任何东西,并且每次使用&lt;&lt;时都会爆炸。这与你想要匹配的概念不符。

你的例子有点做作,可能不适合这个答案,但有些情况需要保证。这是怎么做的。

答案 2 :(得分:0)

在Crazy Eddie的回答中,他提到了SFINAE。现在,C ++ 11内置了它。如果所有类型都派生自相同的基类,那么这非常简单。这是使用C ++ 11的更完整的示例:

#include <type_traits>
#include <iostream>
#include <ostream>

class MyBaseType {};
class MyClass : MyBaseType {};
class MyOtherClass : MyBaseType {};
class SomeType {};

template <typename T>
typename std::enable_if<std::is_base_of<MyBaseType,T>::value,std::ostream&>::type
operator<<(std::ostream& out, const T& x)
{
    out << 1;
}

int main()
{
    MyBaseType x;
    MyClass y;
    MyOtherClass z;
    SomeType i;
    std::cout << x << y << z; // success!
    std::cout << i; // compile-time failure!
}