📄 ei25.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<title>Effective C++, 2E | Item 25: Avoid overloading on a pointer and a numerical type</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 0; setCurrentMax(0);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "EI25_DIR.HTM";
var dingtext = "Item E25, P";
if (self == top) {
top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E25: Don't overload pointers and numbers" -->
<A NAME="6292"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI24_FR.HTM" TARGET="_top">Item 24: Choose carefully between function overloading and parameter defaulting.</A> <BR> Continue to <A HREF="./EI26_FR.HTM" TARGET="_top">Item 26: Guard against potential ambiguity. </A></FONT></DIV>
<P><A NAME="dingp1"></A><FONT ID="eititle">Item 25: Avoid overloading on a pointer and a numerical type.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P>
<A NAME="6294"></A>
<P><A NAME="dingp2"></A>
Trivia question for the day: what is <NOBR>zero?<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="6295"></A>
<P><A NAME="dingp3"></A>
More specifically, what will happen <NOBR>here?<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>
<A NAME="6296"></A>
<UL><PRE>void f(int x);
void f(string *ps);
</PRE>
</UL><A NAME="224160"></A>
<UL><PRE>f(0); // calls f(int) or f(string*)?
</PRE>
</UL><A NAME="224161"></A>
<P><A NAME="dingp4"></A>
<A NAME="p110"></A>The answer is that <CODE>0</CODE> is an <CODE>int</CODE> — a literal integer constant, to be precise — so <CODE>f(int)</CODE> will always be called. Therein lies the problem, because that's not what people always want. This is a situation unique in the world of C++: a place where people think a call should be ambiguous, but compilers do <NOBR>not.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="6300"></A>
<P><A NAME="dingp5"></A>
It would be nice if you could somehow tiptoe around this problem by use of a symbolic name, say, <CODE>NULL</CODE> for null pointers, but that turns out to be a lot tougher than you might <NOBR>imagine.<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="6301"></A>
<P><A NAME="dingp6"></A>
Your first inclination might be to declare a constant called <CODE>NULL</CODE>, but constants have types, and what type should <CODE>NULL</CODE> have? It needs to be compatible with all pointer types, but the only type satisfying that requirement is <CODE>void*</CODE>, and you can't pass <CODE>void*</CODE> pointers to typed pointers without an explicit cast. Not only is that ugly, at first glance it's not a whole lot better than the original <NOBR>situation:<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="6303"></A>
<UL><PRE>
void * const NULL = 0; // potential NULL definition
</PRE>
</UL><A NAME="6304"></A>
<UL><PRE>
f(0); // still calls f(int)
f(static_cast<string*>(NULL)); // calls f(string*)
f(static_cast<string*>(0)); // calls f(string*)
</PRE>
</UL><A NAME="6305"></A>
<P><A NAME="dingp7"></A>
On second thought, however, the use of <CODE>NULL</CODE> as a <CODE>void*</CODE> constant is a shade better than what you started with, because you avoid ambiguity if you use only <CODE>NULL</CODE> to indicate null <NOBR>pointers:<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P>
<A NAME="6306"></A>
<UL><PRE>
f(0); // calls f(int)
f(NULL); // error! — type mis-match
f(static_cast<string*>(NULL)); // okay, calls f(string*)
</PRE>
</UL><A NAME="6308"></A>
<P><A NAME="dingp8"></A>
At least now you've traded a runtime error (the call to the "wrong" <CODE>f</CODE> for 0) for a compile-time error (the attempt to pass a <CODE>void*</CODE> into a <CODE>string*</CODE> parameter). This improves matters somewhat (see <A HREF="./EI46_FR.HTM#195225" TARGET="_top">Item 46</A>), but the cast is still <NOBR>unsatisfying.<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="6314"></A>
<P><A NAME="dingp9"></A>
If you shamefacedly crawl back to the preprocessor, you find that it doesn't really offer a way out, either, because the obvious choices seem to <NOBR>be<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>
<A NAME="6315"></A>
<UL><PRE>#define NULL 0
</PRE>
</UL><A NAME="18020"></A>
<A NAME="dingp10"></A>and<SCRIPT>create_link(10);</SCRIPT>
<A NAME="18024"></A>
<UL><PRE>#define NULL ((void*) 0)
</PRE>
</UL><A NAME="6316"></A>
<P><A NAME="dingp11"></A>
and the first possibility is just the literal <CODE>0</CODE>, which is fundamentally an integer constant (your original problem, as you'll recall), while the second possibility gets you back into the trouble with passing <CODE>void*</CODE> pointers to typed <NOBR>pointers.<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
<A NAME="17462"></A>
<P><A NAME="dingp12"></A>
<A NAME="p111"></A>If you've boned up on the rules governing type conversions, you may know that C++ views a conversion from a <CODE>long</CODE> <CODE>int</CODE> to an <CODE>int</CODE> as neither better nor worse than a conversion from the <CODE>long</CODE> <CODE>int</CODE> <CODE>0</CODE> to the null pointer. You can take advantage of that to introduce the ambiguity into the <CODE>int</CODE>/pointer question you probably believe should be there in the first <NOBR>place:<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="17470"></A>
<UL><PRE>#define NULL 0L // NULL is now a <i>long</i> int
</PRE>
</UL><A NAME="17472"></A>
<UL><PRE>void f(int x);
void f(string *p);
</PRE>
</UL><A NAME="17473"></A>
<UL><PRE>f(NULL); // error! — ambiguous
</PRE>
</UL><A NAME="17476"></A>
<P><A NAME="dingp13"></A>
However, this fails to help if you overload on a <CODE>long</CODE> <CODE>int</CODE> and a <NOBR>pointer:<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="17501"></A>
<UL><PRE>#define NULL 0L
</PRE>
</UL><A NAME="17502"></A>
<UL><PRE>void f(long int x); // this f now takes a long
void f(string *p);
</PRE>
</UL><A NAME="17503"></A>
<UL><PRE>f(NULL); // fine, calls f(long int)
</PRE>
</UL><A NAME="17477"></A>
<P><A NAME="dingp14"></A>
In practice, this is probably safer than defining <CODE>NULL</CODE> to be an <CODE>int</CODE>, but it's more a way of moving the problem around than of eliminating <NOBR>it.<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>
<A NAME="29531"></A>
<P><A NAME="dingp15"></A>
The problem can be exterminated, but it requires the use of a late-breaking addition to the language: <I>member function templates</I> (often simply called <I>member templates</I>). Member function templates are exactly what they sound like: templates within classes that generate member functions for those classes. In the case of <CODE>NULL</CODE>, you want an object that acts like the expression <CODE>static_cast<T*>(0)</CODE> for every type <CODE>T</CODE>. That suggests that <CODE>NULL</CODE> should be an object of a class containing an implicit conversion operator for every possible pointer type. That's a lot of conversion operators, but a member template lets you force C++ into generating them for <NOBR>you:<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>
<A NAME="6322"></A>
<UL><PRE>// a first cut at a class yielding NULL pointer objects
class NullClass {
public:
template<class T> // generates
operator T*() const { return 0; } // operator T* for
}; // all types T; each
// function returns
// the null pointer
</PRE>
</UL><A NAME="6323"></A>
<UL><PRE>
const NullClass NULL; // NULL is an object of
// type NullClass
</PRE>
</UL><A NAME="17544"></A>
<UL><PRE>
void f(int x); // same as we originally had
</PRE>
</UL><A NAME="31792"></A>
<UL><PRE>
void f(string *p); // ditto
</PRE>
</UL><A NAME="6324"></A>
<UL><PRE>
f(NULL); // fine, converts NULL to
// string*, then calls f(string*)
</PRE>
</UL><A NAME="17569"></A>
<P><A NAME="dingp16"></A>
<A NAME="p112"></A>This is a good initial draft, but it can be refined in several ways. First, we don't really need more than one <CODE>NullClass</CODE> object, so there's no reason to give the class a name; we can just use an anonymous class and make <CODE>NULL</CODE> of that type. Second, as long as we're making it possible to convert <CODE>NULL</CODE> to any type of pointer, we should handle pointers to members, too. That calls for a second member template, one to convert <CODE>0</CODE> to type <CODE>T</CODE> <CODE>C::*</CODE> ("pointer to member of type <CODE>T</CODE> in class <CODE>C</CODE>") for all classes <CODE>C</CODE> and all types <CODE>T</CODE>. (If that makes no sense to you, or if you've never heard of — much less used — pointers to members, relax. Pointers to members are uncommon beasts, rarely seen in the wild, and you'll probably never have to deal with them. The terminally curious may wish to consult <A HREF="./EI30_FR.HTM#6599" TARGET="_top">Item 30</A>, which discusses pointers to members in a bit more detail.) Finally, we should prevent clients from taking the address of <CODE>NULL</CODE>, because <CODE>NULL</CODE> isn't supposed to act like a <I>pointer</I>, it's supposed to act like a pointer <I>value</I>, and pointer values (e.g., 0x453AB002) don't have <NOBR>addresses.<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="17562"></A>
<P><A NAME="dingp17"></A>
The jazzed-up <CODE>NULL</CODE> definition looks like <NOBR>this:<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>
<A NAME="17576"></A>
<UL><PRE>
const // this is a const object...
class {
public:
template<class T> // convertible to any type
operator T*() const // of null non-member
{ return 0; } // pointer...
</PRE>
</UL><A NAME="17577"></A>
<UL><PRE>
template<class C, class T> // or any type of null
operator T C::*() const // member pointer...
{ return 0; }
</PRE>
</UL><A NAME="17623"></A>
<UL><PRE>private:
void operator&() const; // whose address can't be
// taken (see <A HREF="./EI27_FR.HTM#6406" TARGET="_top">Item 27</A>)...
</PRE>
</UL><A NAME="17578"></A>
<UL><PRE>
} NULL; // and whose name is NULL
</PRE>
</UL><A NAME="29573"></A>
<P><A NAME="dingp18"></A>
This is truly a sight to behold, though you may wish to make a minor concession to practicality by giving the class a name after all. If you don't, compiler messages referring to <CODE>NULL</CODE>'s type are likely to be pretty <NOBR>unintelligible.<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P>
<A NAME="222208"></A>
<P><A NAME="dingp19"></A>
For another example of how member templates can be useful, take a look at <A HREF="../MEC/MI28_FR.HTM#61766" TARGET="_top">Item M28</A>.<SCRIPT>create_link(19);</SCRIPT>
</P>
<A NAME="17551"></A>
<P><A NAME="dingp20"></A>
An important point about all these attempts to come up with a workable <CODE>NULL</CODE> is that they help only if you're the <I>caller</I>. If you're the <I>author</I> of the functions being called, having a foolproof <CODE>NULL</CODE> won't help you at all, because you can't compel your callers to use it. For example, even if you offer your clients the space-age <CODE>NULL</CODE> we just developed, you still can't keep them from doing <NOBR>this,<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>
<A NAME="17650"></A>
<UL><PRE>
f(0); // still calls f(int),
// because 0 is still an int
</PRE>
</UL><A NAME="17643"></A>
<P><A NAME="dingp21"></A>
<A NAME="p113"></A>and that's just as problematic now as it was at the beginning of this <NOBR>Item.<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="17646"></A>
<P><A NAME="dingp22"></A>
As a designer of overloaded functions, then, the bottom line is that you're best off avoiding overloading on a numerical and a pointer type if you can possibly avoid <NOBR>it.<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI24_FR.HTM" TARGET="_top">Item 24: Choose carefully between function overloading and parameter defaulting.</A> <BR> Continue to <A HREF="./EI26_FR.HTM" TARGET="_top">Item 26: Guard against potential ambiguity. </A></FONT></DIV>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -