Не могу научить компилятор выводить и множества, и векторы

У меня есть две функции, для вывода вектора и множества:

template <typename Element>
ostream& operator<<(ostream& out, const set<Element>& container) {
    bool isnt_first = false;
    for (const Element& element : container) {
        if (isnt_first) {
            out << ", "s << element;
        }
        else {
            out << element;
            isnt_first = true;
        }
    }
    return out;
}

template <typename Element>
ostream& operator<<(ostream& out, const vector<Element>& container) {
    bool isnt_first = false;
    for (const Element& element : container) {
        if (isnt_first) {
            out << ", "s << element;
        }
        else {
            out << element;
            isnt_first = true;
        }
    }
    return out;
}

Функция main:

int main() {

    setlocale(LC_ALL, "ru");

    const set<string> cats = { "Мурка"s, "Белка"s, "Георгий"s, "Рюрик"s };
    cout << cats << endl;
    return 0;
}

Мне нужно вынести функционал вывода в отдельную функцию Print, что я и сделал:

template <typename Container, typename Element>
std::ostream& Print(std::ostream& out, const Container container) {
    bool isnt_first = false;
    for (const Element& element : container) {
        if (isnt_first) {
            out << ", "s << element;
        }
        else {
            out << element;
            isnt_first = true;
        }
    }
    return out;
};

template <typename Element>
ostream& operator<<(ostream& out, const set<Element>& container) {
    out << Print(out, &container);
    return out;
}

template <typename Element>
ostream& operator<<(ostream& out, const vector<Element>& container) {
    out << Print(out, &container);
    return out;
}

Но компилятор выдает ошибки при компиляции:

error C2672: "Print": не найдена соответствующая перегруженная функция
std::ostream &Print(std::ostream &,const Container): не удается составить аргумент шаблон для "Element"

Как заставить программу работать и с множествами, и с векторами?


Ответы (2 шт):

Автор решения: Stanislav Volodarskiy

Вызов Print не упоминает явно тип Element. Компилятор не понимает как его вывести из переданных аргументов. Ему можно подсказать, указав типы явно:

template <typename Container, typename Element>
std::ostream& Print(ostream& out, const Container& container) {
    bool isnt_first = false;
    for (const Element& element : container) {
        if (isnt_first) {
            out << ", " << element;
        }
        else {
            out << element;
            isnt_first = true;
        }
    }
    return out;
}

template <typename Element>
ostream& operator<<(ostream& out, const set<Element>& container) {
    return Print<const set<Element>&, Element>(out, container);
}

template <typename Element>
ostream& operator<<(ostream& out, const vector<Element>& container) {
    return Print<const vector<Element>&, Element>(out, container);
}

Я добавил пропущенный & в декларации Print. И наоборот, убрал его из вызовов. Значение возвращаемое из Print отправлять на печать не надо. Уже всё напечатано.

Можно схитрить и убрать тип Element из декларации Print:

template <typename Container>
std::ostream& Print(ostream& out, const Container& container) {
    bool isnt_first = false;
    // for (const typename Container::value_type& element : container) {
    for (const auto& element : container) {
        if (isnt_first) {
            out << ", " << element;
        }
        else {
            out << element;
            isnt_first = true;
        }
    }
    return out;
}

template <typename Element>
ostream& operator<<(ostream& out, const set<Element>& container) {
    return Print(out, container);
}

template <typename Element>
ostream& operator<<(ostream& out, const vector<Element>& container) {
    return Print(out, container);
}
→ Ссылка
Автор решения: Harry

Я бы предложил вот такой вариант, в котором не нужно ограничиваться только конкретными типами контейнеров, а уж тип элемента — это дело контейнера.

template <typename Container,
          typename = enable_if_t<!is_same_v<Container,string>>>
ostream& operator<<(ostream& out, const Container& container)
{
    bool isnt_first = false;
    for (const auto& element : container)
    {
        if (isnt_first) {
            out << ", "s << element;
        }
        else {
            out << element;
            isnt_first = true;
        }
    }
    return out;
}

См. https://ideone.com/rVOs7J

Главное — объяснить оператору, что string — это таки не контейнер, несмотря на то, что это все же контейнер :)

→ Ссылка