Перегрузка для ostream на векторах выдает ошибку при использовании std :: copy - PullRequest
0 голосов
/ 20 октября 2018

Мне интересно, почему не удается скомпилировать следующее:

#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

Компилируется нормально, когда явыполните one из следующего:

  • Удалите строку с помощью std::copy
  • Измените вектор на одномерный вектор (например, std::vector<unsigned> data(5))).

Что за std::copy вызывает ошибку?


Отладка с помощью clion, это тип вложенного вектора, который печатается вcrbegin строка:

enter image description here

Ответы [ 3 ]

0 голосов
/ 20 октября 2018

Вот эксперимент, который приведет вас к ответу:

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

Sidenote: без определения t он скомпилируется (т. Е. Ваш operator<< будет найден при необходимости), ноесли вы попытаетесь запустить его, он рухнет - попытка использовать std::prev(std::cend(c)) на пустом контейнере не закончится хорошо.

Конечно, это нарушает требования стандарта (определениеваше operator<< внутри пространства имен std, как это не разрешено), но, по крайней мере, для типичных компиляторов теперь будет найдено имя, поэтому код будет скомпилирован.

0 голосов
/ 20 октября 2018

Ваш operator<< невидим для std::ostream_iterator, поэтому он не может вызвать ваш operator<< для элементов контейнера ввода.Просто используйте вместо этого ручной цикл, тогда он будет работать как положено.

Кроме того, std::prev(std::cend(c)) и *(c.crbegin()) равны undefined , когда контейнер пуст, так что следите за этим.

Кроме того, std::vector (как и большинство других стандартных контейнеров) имеет более 1 параметра шаблона, поэтому используйте 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

0 голосов
/ 20 октября 2018

Потому что ваши operator<< не видны для std сущностей.

Примечание std::ostream_iterator<T> выводит значения как бы через operator<< и в соответствии с [temp.dep.res] / 1 :

При разрешении зависимых имен учитываются имена из следующих источников:

  • Объявления, видимые в точке определения шаблона.
  • Объявления из пространств имен, связанных стипы аргументов функции как из контекста экземпляра ([temp.point]), так и из контекста определения.

... ваш operator<< также не виден в точке определенияstd::ostream_iterator<T>, ни в пространстве имен std, поэтому operator<<, используемый в std::ostream_iterator<T>, не может быть правильно разрешен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...