Удаленная функция неявно встроена
(Приложение к существующим ответам)
... И удаленная функция должна быть первым объявлением функции (за исключением удаления явных специализаций шаблонов функций - удаление должно быть при первом объявлении специализации), то есть вы не можете объявить функцию, а затем удалить ее, скажем, в его определении локально для единицы перевода.
Ссылаясь на [dcl.fct.def.delete] / 4 :
Удаленная функция неявно встроена. ( Примечание: правило одного определения ( [basic.def.odr] ) применяется к удаленным определениям. - примечание конца ] Удаленное определение функции должно быть первым объявлением функции или, для явной специализации шаблона функции. первое объявление этой специализации. [Пример:
struct sometype {
sometype();
};
sometype::sometype() = delete; // ill-formed; not first declaration
- конец примера )
Шаблон основной функции с удаленным определением может быть специализированным
Хотя общее правило заключается в том, чтобы избегать специализации шаблонов функций, поскольку специализации не участвуют в первом шаге разрешения перегрузки, существуют спорные условия, в которых это может быть полезно. Например, при использовании не перегруженного шаблона первичной функции без определения для соответствия всем типам, которые не хотелось бы неявно преобразовывать в перегрузку сопоставления путем преобразования; т.е. для неявного удаления ряда совпадений неявного преобразования путем реализации только точных совпадений типов в явной специализации неопределяемого, не перегруженного шаблона первичной функции.
До концепции удаленной функции C ++ 11 можно было сделать это, просто опуская определение шаблона первичной функции, но это давало непонятные неопределенные ссылочные ошибки, которые, вероятно, не давали никакого семантического намерения от автора шаблона первичной функции (намеренно опущено ?). Если вместо этого мы явно удалим шаблон первичной функции, сообщения об ошибках в случае, если не найдена подходящая явная специализация, станут намного приятнее, а также покажут, что пропуск / удаление определения шаблона первичной функции было преднамеренным.
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t);
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
//use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}
Однако вместо того, чтобы просто опустить определение для шаблона первичной функции, приведенного выше, получая скрытую неопределенную ошибку ссылки, когда не найдено явной специализации, определение первичного шаблона можно удалить:
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t) = delete;
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
use_only_explicit_specializations(str);
/* error: call to deleted function 'use_only_explicit_specializations'
note: candidate function [with T = std::__1::basic_string<char>] has
been explicitly deleted
void use_only_explicit_specializations(T t) = delete; */
}
Выводит более читаемое сообщение об ошибке, в котором также четко видна цель удаления (где неопределенная ошибка ссылки может привести к тому, что разработчик посчитает эту ошибку необдуманной).
Возвращаясь к тому, почему мы бы хотели использовать эту технику? Опять же, явные специализации могут быть полезны для неявного удаления неявных преобразований.
#include <cstdint>
#include <iostream>
void warning_at_best(int8_t num) {
std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}
template< typename T >
void only_for_signed(T t) = delete;
template<>
void only_for_signed<int8_t>(int8_t t) {
std::cout << "UB safe! 1 byte, " << +t << "\n";
}
template<>
void only_for_signed<int16_t>(int16_t t) {
std::cout << "UB safe! 2 bytes, " << +t << "\n";
}
int main()
{
const int8_t a = 42;
const uint8_t b = 255U;
const int16_t c = 255;
const float d = 200.F;
warning_at_best(a); // 42
warning_at_best(b); // implementation-defined behaviour, no diagnostic required
warning_at_best(c); // narrowing, -Wconstant-conversion warning
warning_at_best(d); // undefined behaviour!
only_for_signed(a);
only_for_signed(c);
//only_for_signed(b);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = unsigned char]
has been explicitly deleted
void only_for_signed(T t) = delete; */
//only_for_signed(d);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = float]
has been explicitly deleted
void only_for_signed(T t) = delete; */
}