Объявление оператора == для вложенного шаблона класса
У меня есть следующий вложенный класс шаблона внутри другого класса шаблона:
template<typename T>
struct A
{
template<typename V>
struct B {};
};
Какой будет подпись не члена operator==
для вложенного типа B
? Следующая наивная попытка не работает:
template<typename T, typename V>
bool operator==(A<T>::B<V> left, A<T>::B<V> right);
Clang, GCC и MSVC выдают различные ошибки и / или подсказывают, что не так, например, отсутствует template
Ключевое слово, но ни одна из моих попыток решить его не сработала.
Обратите внимание, что это, очевидно, работает:
template<typename T>
struct A
{
template<typename V>
struct B {};
template<typename V>
friend bool operator==(B<V> left, B<V> right)
{
return true;
}
};
Однако причина, по которой мне нужно внешнее объявление, не являющееся членом, заключается в том, чтобы задокументировать его с помощью qdoc. Qdoc использует clang для разбора источников и требует от меня предоставления декларации operator==
что я на самом деле реализовал на месте, как только что показано.
3 ответа
Прямой подход,typename A<T>::template B<V> left
, не работает должным образом, потому чтоA<T>::
является спецификатором вложенного имени, который является невыводимым контекстом.T
необходимо передать явно:operator==<int>(a, b)
.
Хранить==
можно использовать как бинарный оператор, внестрочное объявление, не являющееся членом, можно реализовать с помощью SFINAE:
template <typename T>
struct A {
template <typename V>
struct B {
using ABT = T;
using ABV = V;
};
};
template <typename AB>
std::enable_if_t<
std::is_same_v<AB, typename A<typename AB::ABT>::template B<typename AB::ABV>>,
bool
> operator==(AB left, AB right) {
return true;
}
Полный пример Godbolt: нажмите.
Ошибка не слишком далека, так как вам нужно ключевое слово template, но также typename для обозначения зависимого типа. Рабочий пример будет иметь вид:
template <typename T, typename V>
bool operator==(typename A<T>::template B<V> left,
typename A<T>::template B<V> right) {...}
Хотя я бы предложил вместо этого:
template <typename T, typename V>
using operator_type = typename A<T>::template B<V>;
template <typename T, typename V>
bool operator==(operator_type<T, V> left,
operator_type<T, V> right) {...}
как средство смягчения некоторых эзотерических синтаксисов. Это одна из тех странных вещей, когда вы ожидаете, что typename будет достаточно, чтобы обозначить, что::B является зависимым именем A, но вам все еще нужно ключевое слово шаблона, потому что синтаксический анализатор, как известно, запутывается при работе с <
а также >
, Этот ответ довольно неплохо объясняет, почему:
После того, как name lookup (3.4) обнаружит, что имя является именем шаблона, если за этим именем следует символ <, то <всегда берется как начало списка аргументов шаблона, а не как имя, за которым следует меньше чем оператор.
Теперь мы вернулись к той же проблеме, что и с typename. Что если мы еще не можем знать, является ли имя шаблоном при разборе кода? Нам нужно будет вставить шаблон непосредственно перед именем шаблона, как указано в 14.2/4. Это выглядит так:
t::template f<int>(); // call a function template
Вы можете иметь встроенную декларацию друга и определение схемы
template<typename T>
struct A
{
template<typename V>
struct B
{
friend bool operator==(B left, B right);
};
};
template <typename T, typename V>
bool operator==(typename A<T>::template B<V> left, typename A<T>::template B<V> right)
{
return true;
}
Однако GCC предупреждает
предупреждение: объявление друга
bool operator==(A<T>::B<V>, A<T>::B<V>)
объявляет не шаблонную функцию [-Wnon-template-friend]примечание: (если это не то, что вы хотели, убедитесь, что шаблон функции уже объявлен и добавьте
<>
после имени функции здесь)
И чтобы исправить это предупреждение, мы должны были бы operator==(B left, B right)
до определения B
, который может быть только внутри A
что заставит его быть другом A
также.