C ++ STL не может найти компаратор для вложенного класса

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

#include <algorithm>

template <typename T>
struct Outer
{
struct Inner
{
int x;
};
Inner vec[3];
};

template <typename T>
bool operator <(const typename Outer<T>::Inner& lhs, const typename Outer<T>::Inner& rhs)
{
return lhs.x < rhs.x;
}

int main()
{
Outer<int> out;
Outer<int>::Inner in;
std::lower_bound(out.vec, out.vec + 3, in);
}

GCC 4.4 имеет это, чтобы сказать:

...
bits/stl_algo.h:2442: error: no match for ‘operator<’ in ‘* __middle < __val’

GCC 4.7 печатает намного больше материала, включая вышеперечисленное, заканчивая этим:

...
bits/stl_algobase.h:957:4: note: couldn't deduce template parameter ‘T’

Я готов поверить, что это не совсем правильно сформированный C ++, но почему бы и нет?

2

Решение

Проблема, как вы упомянули, заключается в том, что компилятор не может определить параметр шаблона. T, Это потому, что имя типа Outer :: Inner является не выводимый контекст контекст для Т.

Когда параметр шаблона используется только в не выводимом контексте, он не учитывается для вывода аргумента шаблона. Подробности в разделе 14.8.2.4 Стандарта C ++ (2003).

Зачем? Если вы специализируете Outer как:

template <>
struct Outer<int>
{
struct Inner
{
double y;
};
Inner vec[3];
};

Компилятор не может сделать вывод, должен ли Outer :: Inner ссылаться на это определение или предыдущее.

Теперь о решениях. Есть несколько возможных решений:

  1. Переместить оператора< внутри и сделай это функцией друга.
  2. Определить оператора< внутри Внутренний, как М М. предложил.
  3. Предложено Йоханнес Шауб — Литб: Извлечь внутреннее из базы CRTP, параметризованной внутренним. Затем сделайте тип параметра классом CRTP и приведите ссылку на параметр к производному внутреннему классу, тип которого определяется аргументом шаблона, который вы выводите.

Я опробовал CRTP подход, так как это звучит так круто!

template <typename Inner>
struct Base
{
};

template <typename T>
struct Outer
{
struct Inner : Base<Inner>
{
T x;
};
Inner vec[3];
};

template <typename T>
bool operator< (const Base<T>& lhs, const Base<T>& rhs)
{
return static_cast<const T&>(lhs).x < static_cast<const T&>(rhs).x;
}

Связанные ответы: 1, 2, 3

2

Другие решения

Вот еще один обходной путь.
Почему бы вам не использовать пользовательский компаратор?

template <typename T>
struct Comparer
{
bool operator()(const typename Outer<T>::Inner& lhs, const typename Outer<T>::Inner& rhs)
{
return lhs.x < rhs.x;
}
};

int main()
{
Outer<int> out;
Outer<int>::Inner in;
std::lower_bound(out.vec, out.vec + 3, in, Comparer<int>());
}

Надеюсь, что это работает для вас.

2

Если вы перегружаете конкретный operator< за int проблема исчезнет

bool operator<(const typename Outer<int>::Inner& lhs,
const typename Outer<int>::Inner& rhs)
{
return lhs.x < rhs.x;
}

 

Более простое решение — определение operator< внутри Inner:

template<typename T>
struct Outer
{

struct Inner
{
int x;

bool operator<(const Inner& obj) const
{
return x < obj.x;
}

};
Inner vec[3];
};

Кроме того, это просто быстрое решение. И мой ответ не Зачем Компилятор не может найти operator< во вложенной ситуации в шаблонном режиме.

2

Ответ Mar0ux довольно хорош. Вы можете найти дополнительную информацию здесь:

Стефан Т. Лававей: Core C ++, 2 н

Вы должны посмотреть всю серию видео — она ​​содержит много полезной информации, но вы можете начать с минуты 34 или около того, чтобы получить ответ на свой вопрос. Стивен упоминает одно основное правило, которое нужно иметь в виду:

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

1
По вопросам рекламы [email protected]