Ошибка при последовательном вызове перегрузки enable_if

#c #operator-overloading #c 17 #sfinae #enable-if

#c #оператор-перегрузка #c 17 #sfinae #включить-если

Вопрос:

По сути, идея состоит в том, чтобы перегружать operator << все, что можно итерировать, например векторы, списки и пользовательские классы, правильно определяющие begin() и схему итератора.

Изначально я написал следующий прототип

 template<template<class, class ...> class Container, class T, class ... Whatever>
std::ostreamamp; operator<<(std::ostreamamp; out, const Container<T, Whatever...>amp; container) { stuff }
 

Проблема в том , что я , очевидно , сталкиваюсь с чем — либо , соответствующим определению шаблона и уже перегруженным << , например std::string . Итак, если я пишу cout << string { "Hello" } , это неоднозначно. Я понимаю, почему.

Итак, идея состоит в том, чтобы активировать вышеупомянутую перегрузку тогда и только тогда, когда оператор << еще не определен. Я решил отказаться от этого случая std::enable_if следующим образом:

 template<class> struct sfinae_true : std::true_type {};
template<class ToPrint> static auto test_insertion(int) -> sfinae_true<decltype(std::cout << std::declval<ToPrint>())>;
template<class ToPrint> static auto test_insertion(long) -> std::false_type;
template<class ToPrint> struct is_printable : decltype(test_insertion<ToPrint>(0)) {};
template<class ToPrint> using NotPrintable = std::enable_if_t<! is_printable<ToPrint>::value>;

//overload not availlable if operator<< is already defined (avoids ambiguity)
template<template<class, class ...> class Container, class T, class ... Whatever, NotPrintable<Container<T, Whatever...>>* = nullptr>
std::ostreamamp; operator<<(std::ostreamamp; out, const Container<T, Whatever...>amp; container)
{
    //stuff
}
 

Проблема в том, что каким-то образом, по крайней мере, в C 17, я не могу печатать дважды. Это,

 vector<int> v1 = {1,2,3,4,5};    
vector<int> v2 = {6,7,8};
cout << v1 << endl;
cout << v2 << endl;
 

компилятор сообщает мне, что для версии v2 нет оператора<< … Невероятно , правда ? Я понятия не имею, почему и, следовательно, как устранить проблему.

Реплика: Я знаю, я мог бы просто изменить прототип на just template<class Container> std::ostreamamp; operator<<(std::ostreaamp; out, const Containeramp; c) , но давайте скажем: «Я не хочу».

Полный пример кода для копирования / вставки:

 #include <iostream>
#include <type_traits>
#include <vector>
#include <string>

template<class> struct sfinae_true : std::true_type {};
template<class ToPrint> static auto test_insertion(int) -> sfinae_true<decltype(std::cout << std::declval<ToPrint>())>;
template<class ToPrint> static auto test_insertion(long) -> std::false_type;
template<class ToPrint> struct is_printable : decltype(test_insertion<ToPrint>(0)) {};
template<class ToPrint> using NotPrintable = std::enable_if_t<! is_printable<ToPrint>::value>;

//overload not availlable if operator<< is already defined (avoids ambiguity)
template<template<class, class ...> class Container, class T, class ... Whatever, NotPrintable<Container<T, Whatever...>>* = nullptr>
std::ostreamamp; operator<<(std::ostreamamp; out, const Container<T, Whatever...>amp; container)
{
    out << "{ ";

    auto it = container.begin();
    auto it_end = container.end();

    if(it != it_end)
    {
        out << *it;
          it;
    }

    for(; it != it_end;   it)
        out << " , " << (*it);

    out << " }";

    return out;
}

using namespace std;

int main()
{
    vector<int> v1 = {1,2,3,4,5};    
    vector<int> v2;       

    cout << v1 << endl; //prints {1, 2, 3? 4, 5}
    cout << v2 << endl; //compile error : no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘std::vector<int>’)
    
    cout << string {"Hello"} << endl; //works fine
}
 

Сообщение об ошибке

 error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘std::vector<int>’)
      cout << v2 << endl;
 

Я компилирую с помощью g (Ubuntu 7.5.0-3ubuntu1 ~ 18.04) 7.5.0 с помощью команды g -o sample sample.cpp , где sample.cpp это файл, содержащий приведенный выше код.

Комментарии:

1. Пожалуйста, предоставьте сообщение об ошибке.

2. error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘std::vector<int>’) cout << v2 << endl;

3. Я также отредактировал свой исходный пост, чтобы добавить полный пример кода для копирования / вставки

4. Все еще не могу воспроизвести godbolt.org/z/83h9Gh . Какой компилятор вы используете? Какая версия? Какие флаги компиляции? Кстати, он перестал работать для GCC 8.1 и ниже. Но ошибка также для v1 , а не только v2 .

5. g (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 . Я компилирую с помощью g -std=c 17 -o sample sample.cpp

Ответ №1:

Хорошо, в принципе, моя версия компилятора была слишком старой. В gcc 9.3 все работает так, как задумано.

Тема закрыта. (Я читал, что я не могу закрыть свою собственную тему, если у меня недостаточно репутации, чего у меня нет …)