Answers:
删除函数是C ++ 11的功能:
现在可以直接表达“禁止复制”的常见用语:
class X { // ... X& operator=(const X&) = delete; // Disallow copying X(const X&) = delete; };
[...]
“删除”机制可用于任何功能。例如,我们可以消除这种不希望的转换:
struct Z { // ... Z(long long); // can initialize with an long long Z(long) = delete; // but not anything less };
=delete
即使在可以看到private
方法的上下文中(即在类及其朋友中),也无法访问该方法。这样可以消除您在阅读代码时的不确定性。@Prasoon,第二个示例仍然只是删除构造函数-例如,很高兴看到已删除operator long ()
。
= delete
优于using private
或其他类似的机制,因为通常您希望明显地声明禁用函数并考虑将其用于重载解决方案等,以便它可以尽早失败并向用户提供最明显的错误。任何涉及“隐藏”声明的解决方案都会减少这种影响。
= 0
意味着一个函数是纯虚函数,您不能从此类中实例化一个对象。您需要从中派生并实现此方法= delete
表示编译器不会为您生成那些构造函数。AFAIK仅在复制构造函数和赋值运算符上允许这样做。但是我对即将到来的标准不太满意。=delete
语法还有其他用途。例如,您可以使用它来明确禁止调用可能发生的某种隐式转换。为此,您只需删除重载的函数。有关更多信息,请参见C ++ 0x上的Wikipedia页面。
= delete
并不完全正确。= delete
可以用于任何函数,在这种情况下,它将被显式标记为已删除,并且任何使用都会导致编译器错误。对于特殊的成员函数,这尤其也意味着它们不是由编译器为您生成的,但这仅是被删除的结果,而不是= delete
真正的结果。
摘录自The C ++ Programming Language [第4版]-Bjarne Stroustrup的书讨论了使用以下代码的真正目的=delete
:
3.3.4抑制操作
在层次结构中为类使用默认的复制或移动通常是一种灾难:仅提供指向基础的指针,我们根本不知道派生类具有哪些成员,因此我们不知道如何复制它们。因此,最好的做法通常是删除默认的复制和移动操作,即消除这两个操作的默认定义:
class Shape { public: Shape(const Shape&) =delete; // no copy operations Shape& operator=(const Shape&) =delete; Shape(Shape&&) =delete; // no move operations Shape& operator=(Shape&&) =delete; ˜Shape(); // ... };
现在,编译器将捕获尝试复制Shape的尝试。
该
=delete
机制是通用的,即可以用来抑制任何操作
我使用的编码标准在大多数类声明中都有以下内容。
// coding standard: disallow when not used
T(void) = delete; // default ctor (1)
~T(void) = delete; // default dtor (2)
T(const T&) = delete; // copy ctor (3)
T(const T&&) = delete; // move ctor (4)
T& operator= (const T&) = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)
如果使用这些6中的任何一个,则只需注释掉相应的行即可。
示例:FizzBus类仅需要dtor,因此不要使用其他5。
// coding standard: disallow when not used
FizzBuzz(void) = delete; // default ctor (1)
// ~FizzBuzz(void); // dtor (2)
FizzBuzz(const FizzBuzz&) = delete; // copy ctor (3)
FizzBuzz& operator= (const FizzBuzz&) = delete; // copy assig (4)
FizzBuzz(const FizzBuzz&&) = delete; // move ctor (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign (6)
我们在这里仅注释掉1,然后在其他地方(可能在编码标准建议的地方)安装它的实现。其他5个(共6个)不允许删除。
您还可以使用'= delete'来禁止不同大小的值的隐式提升...示例
// disallow implicit promotions
template <class T> operator T(void) = delete;
template <class T> Vuint64& operator= (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;
新的C ++ 0x标准。请参阅N3242工作草案中的 8.4.3节
(现有答案的附录)
...并且已删除的函数应是该函数的第一个声明(删除函数模板的显式特化除外-删除应在该特化的第一个声明处),这意味着您不能声明一个函数,然后再删除它,例如,按照其定义,仅适用于翻译单位。
删除的函数是隐式内联的。(注:单定义规则([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; */
}
这是C ++ 0x标准中的新事物,您可以在其中删除继承的函数。
void foo(int); template <class T> void foo(T) = delete;
停止所有隐式转换。仅int
接受类型为参数的参数,其他所有参数都将尝试实例化“已删除”函数。