已删除的函数是隐式内联的
(现有答案的附录)
...并且删除的函数应该是函数的第一个声明(除了删除函数模板的显式特化 - 删除应该在特化的第一个声明处),这意味着你不能声明一个函数,然后再删除它,比如说,在翻译单元的本地定义中。
引用[dcl.fct.def.delete]/4:
已删除的函数是隐式内联的。 ( 注意: 单一定义
规则
([basic.def.odr])
适用于已删除的定义。 — end note ] 已删除的定义
函数的第一个声明应该是函数的第一个声明,或者,对于
函数模板的显式特化,第一个
该专业的声明。 [ 例子:
struct sometype {
sometype();
};
sometype::sometype() = delete; // ill-formed; not first declaration
——结束示例)
删除了定义的主函数模板可以特化
尽管一般的经验法则是to avoid specializing function templates,因为特化不参与重载解决的第一步,但在某些情况下它可能有用。例如。当使用没有定义的 非重载 主函数模板来匹配所有不希望隐式转换为其他按转换匹配重载的类型时;即,通过仅在未定义、未重载的主函数模板的显式特化中实现精确类型匹配来隐式删除许多隐式转换匹配。
在 C++11 删除函数概念之前,可以通过简单地省略主函数模板的定义来做到这一点,但这会产生模糊的 undefined reference 错误,可以说没有任何语义意图来自主要功能模板的作者(故意省略?)。如果我们改为显式删除主函数模板,则在没有找到合适的显式特化的情况下的错误消息会变得更好,并且还表明主函数模板的定义的遗漏/删除是故意的。
#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; */
}