为运营商编写模板<<对于任何矢量

时间:2017-07-12 19:46:09

标签: c++ c++11 templates

我正在尝试为任何可迭代容器编写模板运算符。得到一个奇怪的错误:

#include <iostream>

template <typename C>
std::ostream& operator<<(std::ostream& os, const C& c) {
  os << "[";
  for (const auto& v : c) {
    os << v << " ";
  }
  os << "]";
  return os;
}
  

vec.cc:5:6:错误:使用重载运算符'&lt;&lt;'是暧昧的(有   操作数类型'std :: ostream'(又名'basic_ostream')和'const   char [2]')os&lt;&lt; “[”; ~~ ^ ~~~

为什么会出现这个错误?我如何实现我的目标?

7 个答案:

答案 0 :(得分:9)

添加

Pool

template <typename C> std::ostream& operator<<(std::ostream& os, const C& c) { os << "["; for (const auto& v : c) { os << v << " "; } os << "]"; return os; } 的其他global overloads冲突。

要解决此问题,我们可以使用

将模板限制为任何向量而不是任何类型
operator <<

答案 1 :(得分:4)

在这一行:

os << "[";

编译器找到两个有效函数:STL和你的。

为了解决冲突,您需要在模板声明中更具体一些:

template <typename C>
std::ostream& operator<<(std::ostream& os, const std::vector<C>& c) {
  ...

将此扩展到容器通常需要与std::enable_if<>进行一些讨论,这可能会让您更加困惑。我建议您只为要支持的每种容器添加一个重载。

编辑另外,覆盖ostream << T,对于您不拥有的类型通常是一个坏主意,因为它最终会导致冲突。

答案 2 :(得分:3)

您收到此错误的原因是您的功能与左侧<<的{​​{1}}的每次通话都匹配。

std::ostream&

编写template <typename C> std::ostream& operator<<(std::ostream& os, const C& c) { os << "["; for (const auto& v : c) { os << v << " "; } os << "]"; return os; } 时,编译器会找到多个要调用的os << "["函数;你的其中之一就是其中之一。通过添加模板化的全局operator<<,您可以截取每次operator<<的调用。

最简洁的方法是定义一个新功能,比如operator<<

print_collection

如果确实想要定义template <typename C> void print_collection(std::ostream& os, const C& c) { os << "["; for (const auto& v : c) { os << v << " "; } os << "]"; } ,这会变得更加棘手。你可以这样做:

operator<<

但是,如果标准库决定为template <typename C> std::ostream& operator<<(std::ostream& os, const std::vector<C>& c) { os << "["; for (const auto& v : c) { os << v << " "; } os << "]"; return os; } 添加自己的operator<<,则代码将会中断。

我强烈建议您如果要添加此类std::vector,请按照自己的类型进行操作。像这样:

operator<<

然后你可以像这样使用它:

template <typename Iter>
class Range {
    Iter begin_;
    Iter end_;

public:
    Range() = default;
    Range(Iter begin, Iter end)
        : begin_{ begin }
        , end_{ end }
    {}

    auto begin() const { return begin_; }
    auto end() const { return end_; }
};

template <typename Iter>
auto range(Iter begin, Iter end) {
    return Range<Iter>{ begin, end };
}

template <typename C>
auto range(const C& collection) {
    return range(std::begin(collection), std::end(collection));
}

template <typename Iter>
std::ostream& operator<<(std::ostream& os, const Range<Iter>& range) {
    os << "[";
    for (const auto& v : range) {
        os << v << " ";
    }
    os << "]";
    return os;
}

答案 3 :(得分:3)

发生错误是因为您的operator<<与标准库中的许多其他重载相匹配。在你的情况下,重载意味着const char[2]

如果您希望这适用于任何可迭代容器,一种方法是约束它以检查其上begin()end()方法的有效性

#include <iostream>
#include <vector>

using std::cout;
using std::endl;

template <typename Container,
          std::enable_if_t<std::is_same<
            decltype(std::declval<Container>().begin()),
            decltype(std::declval<Container>().begin())>::value>* = nullptr>
std::ostream& operator<<(std::ostream& os, const Container& container) {
    os << "[";
    for (const auto& ele : container) {
        os << ele << " ";
    }
    os << "]";
    return os;
}

int main() {
    auto vec = std::vector<int>{1, 2, 3};
    cout << vec << endl;
}

答案 4 :(得分:2)

我相信你对运营商的定义&lt;&lt;使用为C ++流定义的标准运算符导致歧义。

我尝试使用一些SFINAE技术来改变原型,例如:

template <typename C>
std::ostream& operator<<(std::ostream& os, const C& c, C::const_iterator fakeVar = c.begin() ) {
  os << "[";
  for (const auto& v : c) {
    os << v << " ";
  }
  os << "]";
  return os;
}

只有在可以编译的情况下才能使你的模板适合

答案 5 :(得分:0)

正如其他人所解释的那样,对于os << "[",您的运营商会引入歧义。

我不知道这是不是一个好主意,但我建议使用SFINAE使您的运营商仅支持begin()类型。

我的意思是

template <typename C>
auto operator<<(std::ostream& os, const C& c)
   -> decltype( c.begin(), os ) {
  os << "[";
  for (const auto & v : c) {
    os << v << " ";
  }
  os << "]";
  return os;
}

答案 6 :(得分:0)

模糊性源于您的模板重载了operator<<的{​​{1}},并且可以使用多个定义。

实际上,例如,标准库定义了:

std::ostream

您的函数将与模板替换std::ostream& operator<<(std::ostream&, const char*); 匹配。

为了解决这种歧义,您可以采用另一种设计选择(例如,避免重载typename C = const char*)或解决专门模板功能的歧义。

在这种情况下,许多其他答案都可能是好的,我建议你另一种选择(非常简洁,但有一些限制。见下文)。

由于您正在尝试打印容器,因此容器可能是模板类(operator<<std::vector<T>等...)。因此,可以稍微修改您的函数,以便只处理模板类的类型。

std::list<T>

请注意,由于可变参数模板,代码将需要 C ++ 11

Here一个例子,表明歧义已经解决。

请注意,该解决方案的限制是,它仅适用于作为模板类的容器。它不适用于template <template <typename, typename...> class Container, typename T, typename... Args> std::ostream& operator<<(std::ostream& os, const Container<T, Args...>& c) { os << "["; for (const auto& v : c) { os << v << " "; } os << "]"; return os; } ,因为数组的结构是different