in-class explicit specializations. bug 85282
Весна пришла, прилетели уточки с зимовки, отлетели кукухи...
Будьте осторожны, в офисах фиксируются случаи нападения питонистов на людей! Ни в коем случае не подходите близко и не пытайтесь погладить или покормить с рук дикого разработчика. Выползший к людям обитатель опенспейса может быть переносчиком Poetry или иной заразы. При встрече в узком коридоре, главное, не бегите, не смотрите в глаза и сохраняйте спокойствие. Медленно возьмите у него из рук телефон и разбейте о голову. Желательно не свою. Лишившись возможности писать в рабочие чаты, психический, возможно, потеряет к вам интерес и вернется к работе. Но это не точно. Как писала Анна Каренина в романе "Лев Толстой": "Все здоровые люди похожи друг на друга, каждый поехавший свистит флягой по-своему".
Мне вот снится баг GCC 85282, который нужно обойти применительно к методам класса. Допустим, есть у нас operator(), который для всех типов выполняет одно действие, а для типа int - нечто совершенно иное. Сделать это вне класса очень просто, тут даже GCC не будет возражать:
𝚜𝚝𝚛𝚞𝚌𝚝 𝚄 {
𝚝𝚎𝚖𝚙𝚕𝚊𝚝𝚎 <𝚌𝚕𝚊𝚜𝚜 𝚃>
𝚟𝚘𝚒𝚍 𝚘𝚙𝚎𝚛𝚊𝚝𝚘𝚛()() 𝚌𝚘𝚗𝚜𝚝 {
}
}
Перехватываем оператор для типа int:
𝚝𝚎𝚖𝚙𝚕𝚊𝚝𝚎 <>
𝚟𝚘𝚒𝚍 𝚄::𝚘𝚙𝚎𝚛𝚊𝚝𝚘𝚛()<𝚒𝚗𝚝>() 𝚌𝚘𝚗𝚜𝚝 {
}
Практика не очень чистая, но у стандарта вопросов не вызывает. Другое дело, если мы хотим это сделать внутри класса.
𝚜𝚝𝚛𝚞𝚌𝚝 𝚄 {
𝚝𝚎𝚖𝚙𝚕𝚊𝚝𝚎 <𝚌𝚕𝚊𝚜𝚜 𝚃>
𝚟𝚘𝚒𝚍 𝚘𝚙𝚎𝚛𝚊𝚝𝚘𝚛()() 𝚌𝚘𝚗𝚜𝚝 {
}
𝚝𝚎𝚖𝚙𝚕𝚊𝚝𝚎 <>
𝚟𝚘𝚒𝚍 𝚘𝚙𝚎𝚛𝚊𝚝𝚘𝚛()<𝚒𝚗𝚝>() 𝚌𝚘𝚗𝚜𝚝 {
}
}
А вот так не получится, стандарту это соответствует, а GCC - нет:
𝚎𝚛𝚛𝚘𝚛: 𝚎𝚡𝚙𝚕𝚒𝚌𝚒𝚝 𝚜𝚙𝚎𝚌𝚒𝚊𝚕𝚒𝚣𝚊𝚝𝚒𝚘𝚗 𝚒𝚗 𝚗𝚘𝚗-𝚗𝚊𝚖𝚎𝚜𝚙𝚊𝚌𝚎 𝚜𝚌𝚘𝚙𝚎 '𝚜𝚝𝚛𝚞𝚌𝚝 𝚄'
Попробуем воспользоваться обходным путем из-под Палки.
Если дополнить метод еще одним шаблонным параметром и попробовать специализировать частично, нас ждет разочарование:
𝚝𝚎𝚖𝚙𝚕𝚊𝚝𝚎 <𝚌𝚕𝚊𝚜𝚜 𝚃, 𝚝𝚢𝚙𝚎𝚗𝚊𝚖𝚎 𝚅 = 𝚟𝚘𝚒𝚍>
𝚟𝚘𝚒𝚍 𝚘𝚙𝚎𝚛𝚊𝚝𝚘𝚛()() 𝚌𝚘𝚗𝚜𝚝 {}
𝚝𝚎𝚖𝚙𝚕𝚊𝚝𝚎 <𝚌𝚕𝚊𝚜𝚜 𝚃>
𝚒𝚗𝚕𝚒𝚗𝚎 𝚟𝚘𝚒𝚍 𝚘𝚙𝚎𝚛𝚊𝚝𝚘𝚛()<𝚃, 𝚟𝚘𝚒𝚍>() 𝚌𝚘𝚗𝚜𝚝 {}
Ожидаемо получим только брань компилятора:
𝚎𝚛𝚛𝚘𝚛: 𝚗𝚘𝚗-𝚌𝚕𝚊𝚜𝚜, 𝚗𝚘𝚗-𝚟𝚊𝚛𝚒𝚊𝚋𝚕𝚎 𝚙𝚊𝚛𝚝𝚒𝚊𝚕 𝚜𝚙𝚎𝚌𝚒𝚊𝚕𝚒𝚣𝚊𝚝𝚒𝚘𝚗 '𝚘𝚙𝚎𝚛𝚊𝚝𝚘𝚛()<𝚃, 𝚟𝚘𝚒𝚍>' 𝚒𝚜 𝚗𝚘𝚝 𝚊𝚕𝚕𝚘𝚠𝚎𝚍
Эксперимент, очевидно, неудачный. Проведем другой, для счастливчиков, работающих с c++20
𝚝𝚎𝚖𝚙𝚕𝚊𝚝𝚎 <𝚜𝚝𝚍::𝚜𝚊𝚖𝚎_𝚊𝚜<𝚒𝚗𝚝*> 𝚃>
𝚒𝚗𝚕𝚒𝚗𝚎 𝚟𝚘𝚒𝚍 𝚘𝚙𝚎𝚛𝚊𝚝𝚘𝚛()<𝚃>() 𝚌𝚘𝚗𝚜𝚝 {}
Компилятор опять будет вопить про недопустимость частичной специализации для методов. А если <T> убрать?
𝚝𝚎𝚖𝚙𝚕𝚊𝚝𝚎 <𝚜𝚝𝚍::𝚜𝚊𝚖𝚎_𝚊𝚜<𝚒𝚗𝚝*> 𝚃>
𝚒𝚗𝚕𝚒𝚗𝚎 𝚟𝚘𝚒𝚍 𝚘𝚙𝚎𝚛𝚊𝚝𝚘𝚛()() 𝚌𝚘𝚗𝚜𝚝 {}
Легким движением руки частичная специализация превращается... в элегантную полную. "Ну тогда можно, - скажет GCC и широко улыбнется, - тут просто перегрузка операторов: перекрытие общего более специализированным".
Однако не стоит забывать, что у нас еще есть козыри в широких штанах: контракты!
𝚝𝚎𝚖𝚙𝚕𝚊𝚝𝚎 <𝚌𝚕𝚊𝚜𝚜 𝚃>
𝚒𝚗𝚕𝚒𝚗𝚎 𝚟𝚘𝚒𝚍 𝚘𝚙𝚎𝚛𝚊𝚝𝚘𝚛()() 𝚌𝚘𝚗𝚜𝚝 𝚛𝚎𝚚𝚞𝚒𝚛𝚎𝚜 (𝚜𝚝𝚍::𝚒𝚜_𝚜𝚊𝚖𝚎_𝚟<𝚃, 𝚒𝚗𝚝>) {}
При этом не нужно менять основной operator(), т.к. компилятор считает контрактный оператор более специализированным и выбирает его.
В общем, варианты есть. Баг 85282 не кажется таким уж страшным.
Хотя не сигнализирует ли такой долгий ремонт бага об архитектурных проблемах с лямбдами?