使用std :: copy

时间:2018-10-20 04:08:19

标签: c++ stream operator-overloading c++14

我想知道为什么以下内容无法编译:

#include <vector>
#include <ostream>
#include <iterator>
#include <algorithm>
#include <iostream>

template <template <typename...> class Container, class T>
std::ostream& operator<<(std::ostream& oss, const Container<T>& c) {
    oss << "[";
    std::copy(std::cbegin(c), std::prev(std::cend(c)), std::ostream_iterator<T>(oss, ","));
    return oss << (*std::crbegin(c)) << "]";
}

auto main() -> int {
    std::vector<std::vector<unsigned>> data(5);
    std::cout << data << std::endl;
    return 0;
}

http://coliru.stacked-crooked.com/a/431617423f92ba4e

当我执行以下一项操作时,它可以正常编译:

  • 使用std::copy删除行
  • 将向量更改为一维向量(例如std::vector<unsigned> data(5))。

引起错误的std::copy是什么?


使用clion进行调试,这是在crbegin行中打印的嵌套矢量的类型:

enter image description here

3 个答案:

答案 0 :(得分:3)

因为operator<<实体看不到您的std

注意std::ostream_iterator<T>输出值as if through operator<<,并根据[temp.dep.res]/1

  

在解析从属名称时,来自以下来源的名称是   考虑过:

     
      
  • 在模板定义时可见的声明。
  •   
  • 来自实例化上下文([temp.point])中与函数参数类型相关联的名称空间的声明   并从定义上下文开始。
  •   

...您的operator<<std::ostream_iterator<T>的定义点或命名空间std中都不可见,因此{{1}中使用的operator<< }无法正确解析。

答案 1 :(得分:1)

您的operator<<std::ostream_iterator不可见,因此它无法在输入容器的元素上调用operator<<。只需使用手动循环,即可按预期工作。

此外,当容器为空时,std::prev(std::cend(c))*(c.crbegin()) undefined ,所以要当心。

此外,std::vector(与大多数其他标准容器一样)具有多个模板参数,因此请在操作员的模板参数中使用typename... Ts而不是class T

尝试一下:

#include <vector>
#include <iterator>
#include <iostream>

template < template<typename...> class Container, typename... Ts>
std::ostream& operator<<(std::ostream& oss, const Container<Ts...>& c) {
    oss << "[";
    if (!c.empty()) { // use std::empty() in C++17 and later
        auto last = std::prev(std::cend(c));
        /*
        using value_type = typename Container<Ts...>::value_type;
        std::copy(std::cbegin(c), last, std::ostream_iterator<value_type>(oss, ","));
        */
        for(auto iter = std::cbegin(c); iter != last; ++iter) 
            oss << *iter << ",";
        oss << *last;
    }
    return oss << "]";
}

int main() {
    std::vector<std::vector<unsigned>> data(5);
    std::cout << data << std::endl;
    return 0;
}

输出

[[],[],[],[],[]]

Live Demo

答案 2 :(得分:0)

这是一个实验,可以引导您找到答案:

#include <vector>
#include <ostream>
#include <iterator>
#include <algorithm>
#include <iostream>

namespace std {
template <template <typename...> class Container, class T>
std::ostream& operator<<(std::ostream& oss, const Container<T>& c) {
    oss << "[";
    std::copy(std::cbegin(c), std::prev(std::cend(c)), std::ostream_iterator<T>(oss, ","));
    return oss << (*std::crbegin(c)) << "]";
}
}

int main() {
    std::vector<unsigned> t{ 1, 2, 3 };
    std::vector<std::vector<unsigned>> data(5, t);
    std::cout << data << std::endl;
    return 0;
}

旁注:如果不定义t,它将进行编译(即,您的operator<<将在需要时找到),但是如果您尝试运行它,它将崩溃—尝试使用在空容器上的std::prev(std::cend(c))收尾不好。

当然,这确实违反了标准要求(不允许在这样的命名空间std中定义operator<<),但是至少对于典型的编译器而言,现在可以找到名称,因此代码将编译。