C ++ 20 визначення поза класу в шаблоновому класі


12

До C ++ 20 стандарту C ++, коли ми хотіли визначити позакласного оператора, який використовує деякі приватні члени шаблонного класу, ми будемо використовувати конструкцію, подібну до цього:

template <typename T>
class Foo;

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs);

template <typename T>
class Foo {
public:
    constexpr Foo(T k) : mK(k) {}

    constexpr friend bool operator==<T>(T lhs, const Foo& rhs);

private:
    T mK;
};

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
    return lhs == rhs.mK;
}

int main() {
    return 1 == Foo<int>(1) ? 0 : 1;
}

Оскільки C ++ 20, однак, ми можемо опустити позакласне декларацію, таким чином, і пряму заяву, тому ми можемо піти просто:

template <typename T>
class Foo {
public:
    constexpr Foo(T k) : mK(k) {}

    constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
    T mK;
};

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
    return lhs == rhs.mK;
}

Demo

Тепер моє запитання: яка частина С ++ 20 дозволяє нам це робити? І чому це не було можливо в попередніх стандартах C ++?


Як було зазначено в коментарях, clang не приймає цей код, представлений у демонстраційній версії, що говорить про те, що це може бути помилкою в gcc.

Я подав звіт про помилку на баггілу gcc


2
Я особисто віддаю перевагу у визначенні класу, уникаючи функції шаблону (і відрахування "питання" (Не відповідає "c string" == Foo<std::string>("foo")).
Jarod42

@ Jarod42 Я повністю згоден, я також вважаю за краще визначення в класі. Я просто здивувався, дізнавшись, що C ++ 20 дозволяє нам не повторювати підпис функції тричі при визначенні її ouf-class, що може бути корисно в публічному API, де реалізація знаходиться у прихованому файлі .inl.
ProXicT

Я не помітив, що це неможливо. Як це я використовував досі без проблем?
ALX23z

1
Хммм, у temp.friend , не сильно змінився, особливо не 1.3, який повинен відповідати за таку поведінку. Оскільки Кланг не приймає ваш код, я схиляюся до gcc з помилкою.
n314159

@ ALX23z Працює без позакласного оголошення, якщо клас не шаблонований.
ProXicT

Відповіді:


2

GCC має помилку.

Пошук імен завжди виконується для імен шаблонів, що з’являються перед a <, навіть коли ім'я, про яке йдеться, - це ім'я, оголошене в декларації (friend, явна спеціалізація або явна інстанція).

Оскільки ім'я operator==в декларації про друге є некваліфікованим іменем і підлягає пошуку імен у шаблоні, застосовуються двофазні правила пошуку імен. У цьому контексті operator==це не залежне ім'я (воно не є частиною виклику функції, тому ADL не застосовується), тому ім'я шукається вгору та пов'язане в точці, де воно з'являється (див. [Temp.nondep] параграф 1). Ваш приклад неправильно сформований, оскільки пошук цього імені не знайде декларації operator==.

Я б очікував, що GCC приймає це в режимі C ++ 20 завдяки P0846R0 , який дозволяє (наприклад) operator==<T>(a, b)використовувати в шаблоні, навіть якщо попереднього оголошення operator==як шаблону не видно.

Ось ще цікавіша перевірка:

template <typename T> struct Foo;

#ifdef WRONG_DECL
template <typename T> bool operator==(Foo<T> lhs, int); // #1
#endif

template <typename T> struct Foo {
  friend bool operator==<T>(Foo<T> lhs, float); // #2
};

template <typename T> bool operator==(Foo<T> lhs, float); // #3
Foo<int> f;

З -DWRONG_DECL, GCC та Кланг погоджуються, що ця програма неправильно сформована: некваліфікований пошук для декларації друга №2 в контексті визначення шаблону знаходить декларацію №1, яка не відповідає заявленому другові Foo<int>. Декларація №3 навіть не враховується, оскільки некваліфікований пошук у шаблоні не знаходить її.

З -UWRONG_DECL, GCC (на C ++ 17 і раніше) та Кланг погоджуються, що ця програма неправильно сформована з іншої причини: некваліфікований пошук для operator==лінії №2 нічого не знаходить.

Але, маючи на увазі -UWRONG_DECL, GCC у режимі C ++ 20 вирішує, що нормально, що некваліфікований пошук operator==у №2 не вдається (імовірно, через P0846R0), а потім з'являється, щоб повторити пошук з контексту шаблону шаблону, тепер знаходячи №3, у порушення звичайного правила пошуку двофазних імен для шаблонів.


Дякую за це детальне пояснення, дуже вдало!
ProXicT
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.