Перегрузка + оператор для итераторов и целых чисел без знака - PullRequest
0 голосов
/ 01 сентября 2018

Я пытаюсь перегрузить оператор + для итераторов списка и целых чисел без знака (для практики). Код ниже, кажется, работает нормально. Чтобы объяснить, что он должен делать: если iter является итератором и k целым числом без знака, то iter+k (или operator+(iter, k)) возвращает итератор, полученный при увеличении iter k-раз.

list<int>::iterator operator+(list<int>::iterator iter, unsigned k)
{
    while (k != 0)
    {
        ++iter;
        --k;
    }
    return iter;
}

Однако, когда я шаблонизирую таким образом:

template<typename T>
typename list<T>::iterator 
operator+(typename list<T>::iterator iter, unsigned k)
{
    while (k != 0)
    {
        ++iter;
        --k;
    }
    return iter;
}

и запустите что-нибудь простое, как

int main(){
    list<int> l{1,2};
    list<int>::iterator q = l.begin();
    q+1;
}

Я получаю большую ошибку, которую не могу расшифровать. Я также пытался безрезультатно:

int main(){
    list<int> l{1,2};
    list<int>::iterator q = l.begin();
    operator+<int>(q,1);
}

Буду очень признателен за любые идеи относительно того, почему это происходит и как это исправить!

Ответы [ 2 ]

0 голосов
/ 01 сентября 2018

Это связано с тем, как работает вывод типа шаблона C ++. Из-за того, как настроен язык, компилятор не может автоматически определить, каким должен быть T в контексте list<T>::iterator. Причина в том, что из-за специализации шаблонов теоретически возможно, что два разных экземпляра list могут иметь один и тот же вложенный тип iterator (скажем, list<Pizwkat> и list<Swibble>), и в этом случае компилятор не будет ' не сможет определить, должен ли T быть Pizkwat или Swibble, потому что оба варианта будут правдоподобно работать.

Я не верю, что есть простое решение общей проблемы «добавить operator +, которая работает только для итераторов списка», хотя вы могли бы рассмотреть возможность использования функций std::next и std::advance, которые работают для всех типы итераторов. (Как примечание, обычно не рекомендуется добавлять пользовательские перегрузки для библиотечных типов, поэтому, даже если вы могли бы сделать это, это, вероятно, не будет лучшим решением для любой проблемы, которую вы пытаетесь решить.)

0 голосов
/ 01 сентября 2018

Проблема заключается в том, что параметр типа шаблона T находится в невыгруженном контексте и не может быть выведен. Чтобы сделать это вычет, вы можете сделать это:

template<typename T>
T operator+(T iter, unsigned k)
{ ... }

Обратите внимание, что эта перегрузка потенциально может принести много вреда, поскольку T не ограничивается значением std::list<T>::iterator. SFINAE может помочь:

template<typename T, typename = std::enable_if_t<
    std::is_same_v<T, typename std::list<
    typename std::iterator_traits<T>::value_type>::iterator>>>
T operator+(T iter, unsigned k)
{ ... }

Добавление.

Вопрос, почему operator+<int>(q, 1); терпит неудачу, более тонкий. Во-первых, цитата из The Standard, [temp.arg.explicit] / 8 (см. Также пример там):

Но когда используется шаблон функции с явными аргументами шаблона, вызов не имеет правильной синтаксической формы , если в точке вызова не отображается шаблон функции с этим именем . Если такого имени не видно, вызов синтаксически не правильно сформирован и поиск по аргументам не применяется. Если какое-то такое имя является видимым, применяется поиск, зависящий от аргумента, и дополнительные шаблоны функций могут быть найдены в других пространствах имен.

У нас здесь есть такой шаблон функции, это наш operator+. Следовательно, ADL пытается найти operator+<int> в std::. Однако компиляция завершается неудачно, когда для некоторого шаблона operator+ создается экземпляр <int>. Какой из них и как он не работает, зависит от конкретной реализации библиотеки std.

Например, gcc 8.1 пытается создать экземпляр

template<class _Iterator> constexpr 
std::move_iterator<_IteratorL> std::operator+(
   typename std::move_iterator<_IteratorL>::difference_type,
   const std::move_iterator<_IteratorL>&)
[with _Iterator = int]

и терпит неудачу где-то внутри с

error: no type named 'reference' in 'struct std::iterator_traits<int>'
...

Если мы отключим ADL, он будет работать как положено:

::operator+<int>(q, 1);

или

(operator+<int>)(q, 1);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...