class Widget {
public:
…
template
void processPointer(T* ptr)
{ … }
private:
template<> // error!
void processPointer(void*);
};
The problem is that template specializations must be written at namespace scope, not class scope. This issue doesn't arise for deleted functions, because they don't need a different access level. They can be deleted outside the class (hence at namespace scope):
class Widget {
public:
…
template
void processPointer(T* ptr)
{ … }
};
template<> // still
void Widget::processPointer(void*) = delete; // public,
// but
// deleted
The truth is that the C++98 practice of declaring functions private
and not defining them was really an attempt to achieve what C++11's deleted functions actually accomplish. As an emulation, the C++98 approach is not as good as the real thing. It doesn't work outside classes, it doesn't always work inside classes, and when it does work, it may not work until link-time. So stick to deleted functions.
Things to Remember
• Prefer deleted functions to private undefined ones.
• Any function may be deleted, including non-member functions and template instantiations.
Item 12: Declare overriding functions override
.
The world of object-oriented programming in C++ revolves around classes, inheritance, and virtual functions. Among the most fundamental ideas in this world is that virtual function implementations in derived classes override the implementations of their base class counterparts. It's disheartening, then, to realize just how easily virtual function overriding can go wrong. It's almost as if this part of the language were designed with the idea that Murphy's Law wasn't just to be obeyed, it was to be honored.
Because “overriding” sounds a lot like “overloading,” yet is completely unrelated, let me make clear that virtual function overriding is what makes it possible to invoke a derived class function through a base class interface:
class Base {
public:
virtual void doWork(); // base class virtual function
…
};
class Derived: public Base {
public:
virtual void doWork(); // overrides Base::doWork
… // ("virtual" is optional
}; // here)
std::unique_ptr< Base> upb = // create base class pointer
std::make_unique< Derived>(); // to derived class object;
// see Item 21 for info on
… // std::make_unique
upb->doWork(); // call doWork through base
// class ptr; derived class
// function is invoked
For overriding to occur, several requirements must be met:
• The base class function must be virtual.
• The base and derived function names must be identical (except in the case of destructors).
• The parameter types of the base and derived functions must be identical.
• The const
ness of the base and derived functions must be identical.
• The return types and exception specifications of the base and derived functions must be compatible.
To these constraints, which were also part of C++98, C++11 adds one more:
• The functions' reference qualifiers must be identical. Member function reference qualifiers are one of C++11's less-publicized features, so don't be surprised if you've never heard of them. They make it possible to limit use of a member function to lvalues only or to rvalues only. Member functions need not be virtual to use them:
class Widget {
public:
…
void doWork() &; // this version of doWork applies
// only when *this is an lvalue
void doWork() &&; // this version of doWork applies
}; // only when *this is an rvalue
…
Widget makeWidget(); // factory function (returns rvalue)
Widget w; // normal object (an lvalue)
…
w.doWork(); // calls Widget::doWork for lvalues
// (i.e., Widget::doWork &)
makeWidget().doWork(); // calls Widget::doWork for rvalues
// (i.e., Widget::doWork &&)
I'll say more about member functions with reference qualifiers later, but for now, simply note that if a virtual function in a base class has a reference qualifier, derived class overrides of that function must have exactly the same reference qualifier. If they don't, the declared functions will still exist in the derived class, but they won't override anything in the base class.
All these requirements for overriding mean that small mistakes can make a big difference. Code containing overriding errors is typically valid, but its meaning isn't what you intended. You therefore can't rely on compilers notifying you if you do something wrong. For example, the following code is completely legal and, at first sight, looks reasonable, but it contains no virtual function overrides — not a single derived class function that is tied to a base class function. Can you identify the problem in each case, i.e., why each derived class function doesn't override the base class function with the same name?
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &
void mf4() const;
};
class Derived: public Base {
public:
virtual void mf1();
virtual void mf2(unsigned int x);
virtual void mf3() &&
void mf4() const;
};
Need some help?
• mf1
is declared const
in Base
, but not in Derived
.
• mf2
takes an int
in Base
, but an unsigned int
in Derived
.
• mf3
is lvalue-qualified in Base
, but rvalue-qualified in Derived
.
• mf4
isn't declared virtual
in Base
.
You may think, “Hey, in practice, these things will elicit compiler warnings, so I don't need to worry.” Maybe that's true. But maybe it's not. With two of the compilers I checked, the code was accepted without complaint, and that was with all warnings enabled. (Other compilers provided warnings about some of the issues, but not all of them.)
Because declaring derived class overrides is important to get right, but easy to get wrong, C++11 gives you a way to make explicit that a derived class function is supposed to override a base class version: declare it override
. Applying this to the example above would yield this derived class:
class Derived: public Base {
public:
virtual void mf1() override;
virtual void mf2(unsigned int x) override;
virtual void mf3() && override;
virtual void mf4() const override;
Читать дальше