Чому обрано таке перевантаження оператора перетворення?


27

Розглянемо наступний код .

struct any
{
    template <typename T>
    operator T &&() const;

    template <typename T>
    operator T &() const;
};
int main()
{
    int a = any{};
}

Тут другий оператор перетворення вибирається за роздільною здатністю перевантаження. Чому?

Наскільки я розумію, два оператори виводяться на operator int &&() constтаoperator int &() const відповідно. Обидва знаходяться в наборі життєздатних функцій. Читання [over.match.best] не допомогло мені зрозуміти, чому останній краще.

Чому останній функціонує краще, ніж перший?


Я проста кішка, і я б сказав, що це, мабуть, буде другим, і введення іншого мало б кардинальну зміну C ++ 11. Це буде десь у стандарті. Хороше питання, проте, висловіть нагоду. (Експерти уваги: ​​якщо цей коментар - хогва, то скажіть, будь ласка!)
— Вірсавія

3
FWIW, template <typename T> operator T &&() const &&; template <typename T> operator T &() const &;отримує дзвінок першим.
— NathanOliver

5
@LightnessRaceswithMonica Оператори перетворення дозволяють це в іншому випадку не було б можливості мати кілька операторів перетворення.
— NathanOliver

1
@Bathsheba Я не думаю, що це причина. Це як би сказати, що конструктори переміщення ніколи не можна вибирати за допомогою роздільної здатності перевантаження, тому що це буде переломною зміною. Якщо ви пишете конструктор переміщення, ви вказуєте, що ваш код "порушений" цим визначенням.
— Брайан

1
@LightnessRaceswithMonica Те, як я тримаю це прямо в голові, я вважаю тип повернення як назву функції. Різні типи дорівнює різним іменам дорівнює успіху :)
— NathanOliver

Відповіді:


13

Оператор перетворення, який повертається, T&є кращим, оскільки він більш спеціалізований, ніж оператор перетворення, який повертається T&&.

Дивіться C ++ 17 [temp.deduct.partial] / (3.2):

У контексті виклику функції перетворення використовуються типи повернення шаблонів функції перетворення.

та / 9:

Якщо для даного типу виведення досягає успіху в обох напрямках (тобто типи однакові після перетворень вище) і в обох, Pі Aбули еталонними типами (перед тим, як замінити тип, згаданий вище): - якщо тип із шаблону аргументу була посиланням на значення і тип з шаблону параметра не був, тип параметра не вважається принаймні таким спеціалізованим, як тип аргументу; ...


12

Виведені оператори конверсії повернутої вартості трохи дивні. Але основна ідея полягає в тому, що вона діє як параметр функції, щоб вибрати, який з них використовувати.

І при виборі між T&&і T&на T&перемоги в правилах дозволу перевантаження. Це дозволить:

template<class T>
void f( T&& ) { std::cout << "rvalue"; }
template<class T>
void f( T& ) { std::cout << "lvalue"; }

працювати. T&&може відповідати рівню lvalue, але коли наявні як значення lvalue, так і універсальні еталонні перевантаження, переважніше значення lvalue.

Можливо, правильний набір операторів перетворення:

template <typename T>
operator T&&() &&;

template <typename T>
operator T &() const; // maybe &

або навіть

template <typename T>
operator T() &&;

template <typename T>
operator T &() const; // maybe &

щоб запобігти покусі вас невдале продовження терміну служби.

3 Типи, що використовуються для визначення впорядкування, залежать від контексту, в якому здійснюється часткове замовлення:

[SNIP]

(3.2) У контексті виклику функції перетворення використовуються типи повернення шаблонів функцій перетворення.

Потім закінчується залежно від "більш спеціалізованих" правил під час вибору перевантажень:

(9.1) якщо тип із шаблону аргументу був еталонним значенням і тип з шаблону параметра не був, тип параметра не вважається принаймні таким же спеціалізованим, як тип аргументу; інакше,

Таким чином , operator T&&не по крайней мере спеціалізовані , як operator T&, між тим немає правил держави operator T&не є , принаймні , як спеціалізована operator T&&, тому operator T&більш спеціалізовані , ніж operator T&&.

Більш спеціалізовані шаблони виграють роздільну здатність перевантаження над меншим, а все інше рівним.


Хтось додав тег мови-юриста, коли я відповідав; Я міг би просто видалити. У мене розпливчаста пам'ять про читання тексту, в якому йдеться про те, що він розглядається як параметр для вибору того, хто називається, і текст, який говорить про те, як &&проти &поводжуться під час вибору перевантажених шаблонів, але дражнити точний текст може зайняти деякий час .
— Якк - Адам Невраумон

4

Ми намагаємося ініціалізувати intз any. Процес для цього:

  1. Зробити все, як ми можемо це зробити. Тобто визначте всіх наших кандидатів. Вони походять від не явних функцій перетворення, які можна перетворити за intдопомогою стандартної послідовності перетворення ( [over.match.conv] ). Розділ містить цю фразу:

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

  2. Виберіть найкращого кандидата.

Після кроку 1 у нас є два кандидати. operator int&() constі operator int&&() constобидва вони вважаються врожайними intдля вибору функцій кандидата. Який найкращий кандидат поступився int?

У нас є tiebreaker, який віддає перевагу посиланням lvalue на посилання rvalue ( [over.ics.rank] /3.2.3 ). Тут ми дійсно не пов'язуємо посилання, і приклад там дещо перевернутий - це для випадку, коли параметр є посиланням lvalue vs rvalue.

Якщо це не стосується, ми переходимо до [over.match.best] /2.5 крайового прориву віддати перевагу більш спеціалізованому шаблону функцій.

Взагалі кажучи, велике правило - це більш конкретна конверсія - найкраща відповідність. Функція перетворення опорних значень lvalue є більш специфічною, ніж функція перетворення опорного пересилання, тому вона є кращою. Немає нічого про те, що intми ініціалізуємо, що вимагає ревальвації (якби ми замість цього ініціалізували int&&, тоді operator T&() constне було б кандидатом).


3.2.3 насправді каже, T&&виграє чи не так? Ах, але у нас немає поваги. Або ми? Обираючи наших кандидатів, деякі слова, мабуть, визначали, як ми потрапили int&і int&&чи це було обов'язковим?
— Якк - Адам Невраумон

@ Yakk-AdamNevraumont Ні, цей варіант надає перевагу посиланням lvalue на посилання rvalue. Я думаю, що це, мабуть, неправильний розділ у будь-якому випадку, і "більш спеціалізований" краватка є правильним.
— Баррі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.