Usunięta funkcja jest domyślnie wbudowana
(Dodatek do istniejących odpowiedzi)
... A usunięta funkcja będzie pierwszą deklaracją funkcji (z wyjątkiem usuwania wyraźnych specjalizacji szablonów funkcji - usunięcie powinno nastąpić przy pierwszej deklaracji specjalizacji), co oznacza, że nie można zadeklarować funkcji, a następnie usunąć, powiedzmy, w swojej definicji lokalnej dla jednostki tłumaczeniowej.
Powołując się na [dcl.fct.def.delete] / 4 :
Usunięta funkcja jest domyślnie wbudowana. ( Uwaga: Reguła z jedną definicją ( [basic.def.odr] ) ma zastosowanie do usuniętych definicji. - uwaga końcowa] Usunięta definicja funkcji powinna być pierwszą deklaracją funkcji lub, w przypadku wyraźnej specjalizacji szablonu funkcji , pierwsza deklaracja tej specjalizacji. [Przykład:
struct sometype {
sometype();
};
sometype::sometype() = delete; // ill-formed; not first declaration
- koniec przykładu )
Szablon funkcji podstawowej z usuniętą definicją może być wyspecjalizowany
Chociaż ogólną zasadą jest unikanie specjalizacji szablonów funkcyjnych, ponieważ specjalizacje nie biorą udziału w pierwszym etapie rozwiązywania problemu przeciążenia, istnieją pewne konteksty, w których może to być przydatne. Np. Przy użyciu nie przeciążonego szablonu funkcji podstawowej bez definicji, aby dopasować wszystkie typy, których nie chciałoby się pośrednio konwertować na przeciążenie polegające na dopasowaniu przez konwersję; tj. w celu niejawnego usunięcia szeregu niejawnych dopasowań konwersji poprzez implementację tylko dopasowań dokładnych typów w wyraźnej specjalizacji niezdefiniowanego, nie przeciążonego szablonu funkcji podstawowej.
Przed koncepcją usuniętej funkcji w C ++ 11 można to zrobić, po prostu pomijając definicję szablonu funkcji podstawowej, ale dawało to niejasne, nieokreślone błędy referencyjne, które prawdopodobnie nie dawały żadnej intencji semantycznej autorowi szablonu funkcji podstawowej (celowo pominięto ?). Jeśli zamiast tego jawnie usuniemy szablon funkcji podstawowej, komunikaty o błędach w przypadku, gdy nie zostanie znaleziona odpowiednia wyraźna specjalizacja, staną się znacznie przyjemniejsze, a także pokażą, że pominięcie / usunięcie definicji szablonu funkcji podstawowej było celowe.
#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< ...
}
Jednak zamiast po prostu pominąć definicję powyższego szablonu funkcji podstawowej, powodując niejasny, niezdefiniowany błąd odniesienia, gdy żadna wyraźna specjalizacja nie pasuje, podstawową definicję szablonu można usunąć:
#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; */
}
Daje bardziej czytelny komunikat o błędzie, w którym zamiar usunięcia jest również wyraźnie widoczny (gdzie nieokreślony błąd odniesienia może prowadzić do tego, że programista uzna to za nierozważny błąd).
Wracając do tego, dlaczego mielibyśmy chcieć korzystać z tej techniki? Ponownie, wyraźne specjalizacje mogą być przydatne do niejawnego usuwania niejawnych konwersji.
#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; */
}