Một chức năng bị xóa là ngầm định
(Phụ lục cho câu trả lời hiện có)
... Và một hàm bị xóa sẽ là khai báo đầu tiên của hàm (ngoại trừ việc xóa các chuyên môn rõ ràng của các mẫu hàm - việc xóa phải nằm ở phần khai báo đầu tiên của chuyên môn hóa), nghĩa là bạn không thể khai báo một hàm và sau đó xóa nó, nói, theo định nghĩa của nó cục bộ cho một đơn vị dịch thuật.
Trích dẫn [dcl.fct.def.delete] / 4 :
Một chức năng bị xóa là ngầm định. ( Lưu ý: Quy tắc một định nghĩa ( [basic.def.odr] ) áp dụng cho các định nghĩa đã xóa. - lưu ý cuối ] Định nghĩa đã xóa của hàm sẽ là khai báo đầu tiên của hàm hoặc, đối với chuyên môn hóa rõ ràng của mẫu hàm , tuyên bố đầu tiên của chuyên ngành đó. [Ví dụ:
struct sometype {
sometype();
};
sometype::sometype() = delete; // ill-formed; not first declaration
- ví dụ cuối )
Mẫu hàm chính với định nghĩa đã xóa có thể được chuyên biệt
Mặc dù một nguyên tắc chung là tránh các mẫu chức năng chuyên dụng vì các chuyên môn không tham gia vào bước đầu tiên của giải quyết quá tải, có thể tranh cãi một số bối cảnh có thể hữu ích. Ví dụ: khi sử dụng không quá tải mẫu hàm chính không có định nghĩa để khớp với tất cả các loại mà người ta không muốn chuyển đổi hoàn toàn sang quá tải chuyển đổi khớp bằng cách khác; tức là, để loại bỏ hoàn toàn một số đối sánh chuyển đổi ngầm định bằng cách chỉ thực hiện khớp chính xác loại trong việc chuyên môn hóa rõ ràng của mẫu hàm chính không được xác định, không quá tải.
Trước khi khái niệm chức năng bị xóa của C ++ 11, người ta có thể làm điều này bằng cách bỏ qua định nghĩa của mẫu hàm chính, nhưng điều này đã làm cho các lỗi tham chiếu không xác định được cho là không có ý nghĩa ngữ nghĩa nào từ tác giả của mẫu hàm chính (bỏ qua một cách có chủ ý ?). Thay vào đó, nếu chúng ta xóa rõ ràng mẫu chức năng chính, các thông báo lỗi trong trường hợp không tìm thấy chuyên môn rõ ràng phù hợp sẽ trở nên đẹp hơn nhiều và cũng cho thấy việc bỏ sót / xóa định nghĩa của mẫu chức năng chính là cố ý.
#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< ...
}
Tuy nhiên, thay vì chỉ đơn giản là bỏ qua một định nghĩa cho mẫu hàm chính ở trên, mang lại một lỗi tham chiếu không xác định tối nghĩa khi không có chuyên môn rõ ràng phù hợp, định nghĩa mẫu chính có thể bị xóa:
#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; */
}
Mang lại một thông báo lỗi dễ đọc hơn, trong đó mục đích xóa cũng được hiển thị rõ ràng (trong đó một lỗi tham chiếu không xác định có thể dẫn đến nhà phát triển nghĩ rằng đây là một lỗi không suy nghĩ).
Trở lại lý do tại sao chúng ta sẽ muốn sử dụng kỹ thuật này? Một lần nữa, các chuyên ngành rõ ràng có thể hữu ích để ngầm loại bỏ chuyển đổi tiềm ẩn.
#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; */
}