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


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.