Things to Remember:
- 比起private和undefined,更好使用delected functions
- 任何函数都可以被deleted,包括非成员函数和模板实例化
如果你正在给其他的程序员提供code,但是你不想他们调用一些特定的函数,那么最简单的办法就是不要声明这些函数,没有声明就没有调用。但是有些时候,C++会为你声明一些函数,如果你想要阻止客户端调用这些函数就不太容易了。
C++98阻止使用function的方法是声明他们为priavte并且不定义他们。
例如iostream在C++标准库中的basic_ios。所有的istream和ostream class都继承自此类。复制istream和ostream是不被允许的,因为我们并不知道这样的操作应该做什么。一个istream对象,代表了一个输入值stream,一些值可能已经被读取了,一些值是潜在将会被读取的。如果一个istream被复制了,那么是否需要复制已经读过的所有值和将要读取的值?最简单的方式就是定义为不存在。禁止复制流就是这么做的。
使istream和ostream class不可复制,basic_ios在C++98中指定如下:
template<class charT,class traits = char_traits<char T>>
class basic_ios:public ios_base{
public:
...
private:
basic_ios(const basic_ios&);//not define
basic_ios& operator=(const basic_ios&); //not define
};
声明这些函数为private可以防止客户端调用他们。故意的不声明这些函数,当任然调用到他们之后(比如member function或者 friend of class),因为丢失函数定义,链接将会失效。
在C++11中 ,有更好的方式去实现相同的结果:“= delect”去标记复制构造函数和赋值操作符作为 delected functions。Delected function不能使用任何方法使用这个函数,即使是member和friend函数也是在尝试复制的时候编译失败。这是在C++98上的一种提升,C++98是当使用指导link-time的时候才会去诊断。
template<class charT,class traits = char_traits<char T>>
class basic_ios:public ios_base
{
public:
basic_ios(const basic_ios&) = delecte;
basic_ios& operator=(const basic_ios&)=delect;
};
一般来说,deleted functions被声明为public,而不是private。当客户端代码尝试去使用一个member 函数的时候,C++在删除状态之前检查可访问性。当客户端code尝试使用一个deleted private function,一些编译器只是会提示函数是私有的,尽管这个私有并不是真正的禁止访问的原因。所以将函数公开将会更好的看到编译器提示错误的消息。
一个重要的deleted function的好处就是任何!函数都可以是delected,但是C++98只有成员函数可以是private。
如下面的例子,我们希望传入的参数值真正的int。但是C++继承的C意味着几乎所有的类型都可以被看做是模糊的数值,可以被隐式转换为int。实现这个的方法是要为过滤掉的类型实现deleted overloads。虽然不能使用这些deleted函数,但是它们还是code的一部分,因为在overload resolution的时候要考虑它们。这就是为什么下列函数在调用的时候会被拒绝。
bool isLucky(int number);
bool isLucky(char) = delete;//reject chars
bool isLucky(bool) = delete;
bool isLucky(double) = delete;//reject doubles and floats
另一个C++98不能实现而delected function可以实现的好处就是,禁止使用应该被禁止的模板实例化。
在指针的世界中,有两种特殊的指针。一种是void*,因为没有办法解引用它,也没有办法增加或者删除它;另一种就是char*,因为它通常是指向C风格的string而不是单个字符的指针。这些情况是需要特殊处理的。我们假设当前例子是拒绝传入void*和char*这两种指针。
template<typename T>
void processPointer(T* ptr);
//easy enforced
template<>
void processPointer<void>(void*) = delete;
template<>
void processPointer<const void>(const void*) = delete;
template<>
void processPointer<const char>(const char*) = delete;
如果你有一个template func在class内,你想要通过声明一些实例化通过声明private,我们不能这么做,因为在同一个类中,对于成员函数template跟主template有不同的level是不可能的!这里的问题是在于,模板特化(template specialization)必须是被写在namespace作用域中,而不是class作用域。对应已删除的函数不会出现这种问题,因为他们不需要不同的访问等级。他们可以在class外被deleted(在作用域内)。
class Widget
{
public:
template<typename T>
void processPointer(T* ptr){}
private:
template<>
void processPointer<void>(void*);//error!
};
template<>
void Widget::processPointer<void>(void*) = delete;//still public,but deleted
坚定的选择使用deleted function!事实上,C++98声明private func,不真正的定义函数,真是C++11的deleted func实际上做的。但是C++98的方法不如C++11的好。C++98不能在class外部工作,在内部也不总是有用,当它产生用处的时候,就是可能已经在link-time了。