Определить оператор шаблона << для итераций - PullRequest
0 голосов
/ 12 ноября 2018

Я могу определить и использовать:

std::ostream& operator<<(std::ostream& os, std::vector<int> const& container)
{
    for (auto const& n : container)
        os << n << ", ";
    return os;
}

int main()
{
    std::vector<int> data{0,1,2};
    std::cout << data << '\n';
}

( демо )

Но определение этого оператора не зависит от того, какой контейнер я использую. Оттуда я бы хотел определить шаблонную версию:

template<class Iterable>
std::ostream& operator<<(std::ostream& os, Iterable const& iterable)
{
    for (auto const& n : iterable)
        os << n << ", ";
    return os;
}

int main()
{
    std::vector<int> data{0,1,2};
    std::cout << data << '\n';
}

( демо )

Вот где мой компилятор злится и многословно отвергает его:

error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'char')

... с большим количеством возможных кандидатов.

Почему это не законно и как я могу определить такого оператора?

1 Ответ

0 голосов
/ 12 ноября 2018

Как указано в этом другом вопросе StackOverflow, Как исправить ошибку «неоднозначная перегрузка» при перегрузке оператора , при определении operator<<(std::ostream&, T) для всех T вы перегружаете его для типов, где существует существующий operator<<. Отсюда и неоднозначный вызов:

os << n << ", ";
        ^-- recursively calls itself? or calls the overload provided by the Standard Library?

Решение состоит в том, чтобы использовать SFINAE , чтобы гарантировать, что вы определяете свою перегрузку только для повторяемых типов. Поскольку определение на основе диапазона для цикла основано на begin и end, мы можем использовать его для различения того, что является Iterable:

template<class Iterable, class = std::void_t<decltype(begin(std::declval<Iterable>()))>>
std::ostream& operator<<(std::ostream& os, Iterable const& iterable)
{
    for (auto const& n : iterable)
        os << n << ", ";
    return os;
}

( демо )

Теперь std::cout << data вызывает вашу версию, а std::cout << '\n' вызывает встроенную перегрузку, так как замена не выполняется для Iterable = char: begin(char) не определено.

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