Why Not Specialize Function Templates?This article appeared in C/C++ Users Journal, 19(7), July 2001. 原文在此: http://www.gotw.ca/publications/mill17.htm While the title of this article is a question, it could also be made into a statement: this article is about when and why not to specialize templates. The Important Difference: Overloading vs. SpecializationIt‘s important to make sure we have the terms down pat, so here‘s a quick review. In C++, there are class templates and function templates. These two kinds of templates don‘t work in exactly the same ways, and the most obvious difference is in overloading: Plain old C++ classes don‘t overload, so class templates don‘t overload either. On the other hand, plain old C++ functions having the same name do overload, and so function templates are allowed to overload too. This is pretty natural. What we have so far is summarized in Example 1: // Example 1: Class vs. function template, and overloading // A class template // A function template with two overloads These unspecialized templates are also called the underlying base templates. Further, base templates can be specialized. This is where class templates and function templates diverge further, in ways that will become important later in this article. A class template can be partially specialized and/or fully specialized.[1] A function template can only be fully specialized, but because function templates can overload we can get nearly the same effect via overloading that we could have got via partial specialization. The following code illustrates these differences: // Example 1, continued: Specializing templates // A partial specialization of (a) for pointer types // A full specialization of (a) for int // A separate base template that overloads (b) and (c) // A full specialization of (b) for int // A plain old function that happens to overload with Finally, let‘s focus on function templates only and consider the overloading rules to see which ones get called in different situations. The rules are pretty simple, at least at a high level, and can be expressed as a classic two-class system:
Putting these rules together, here‘s a sample of what we get: // Example 1, continued: Overload resolution f( b ); // calls (b) with T = bool So far I‘ve deliberately chosen simpler cases, because here‘s where we step off into the deep end of the pool. Why Not Specialize: The Dimov/Abrahams ExampleConsider the following code: // Example 2: Explicit specialization template<class T> // (b) a second base template, overloads (a) template<> // (c) explicit specialization of (b) // ... int *p; The result for the last line in Example 2 is just what you‘d expect. The question of the day, however, is why you expected it. If you expected it for the wrong reason, you will be very surprised by what comes next. After all, "So what," someone might say, "I wrote a specialization for a pointer to int, so obviously that‘s what should be called" - and that‘s exactly the wrong reason. Consider now the following code, put in this form by Peter Dimov and Dave Abrahams: // Example 3: The Dimov/Abrahams Example template<> // (c) explicit specialization, this time of (a) template<class T> // (b) a second base template, overloads (a) // ... int *p; If this surprises you, you‘re not alone; it has surprised a lot of experts in its time. The key to understanding this is simple, and here it is: Specializations don‘t overload. Only the base templates overload (along with nontemplate functions, of course). Consider again the salient part from the summary I gave above of the overload resolution rules, this time with specific words highlighted:
Overload resolution only selects a base template (or a nontemplate function, if one is available). Only after it‘s been decided which base template is going to be selected, and that choice is locked in, will the compiler look around to see if there happens to be a suitable specialization of that template available, and if so that specialization will get used. Important MoralsIf you‘re like me, the first time you see this you‘ll ask the question: "Hmm. But it seems to me that I went and specifically wrote a specialization for the case when the parameter is an int*, and it is an int* which is an exact match, so shouldn‘t my specialization always get used?" That, alas, is a mistake: If you want to be sure it will always be used in the case of exact match, that‘s what a plain old function is for -- so just make it a function instead of a specialization. The rationale for why specializations don‘t participate in overloading is simple, once explained, because the surprise factor is exactly the reverse: The standards committee felt it would be surprising that, just because you happened to write a specialization for a particular template, that it would in any way change which template gets used. Under that rationale, and since we already have a way of making sure our version gets used if that‘s what we want (we just make it a function, not a specialization), we can understand more clearly why specializations don‘t affect which template gets selected.
But what if you‘re the one who‘s writing, not just using, a function template? Can you do better and avoid this (and other) problem(s) up front, for yourself and for your users? Indeed you can:
// Example 4: Illustrating Moral #2 template<class T> template<class T> SummaryIt‘s okay to overload function templates. Overload resolution considers all base templates equally and so it works as you would naturally expect from your experience with normal C++ function overloading: Whatever templates are visible are considered for overload resolution, and the compiler simply picks the best match. It‘s a lot less intuitive to specialize function templates. For one thing, you can‘t partially specialize them -- pretty much just because the language says you can‘t.[2] For another thing, function templatespecializations don‘t overload. This means that any specializations you write will not affect which template gets used, which runs counter to what most people would intuitively expect. After all, if you had written a nontemplate function with the identical signature instead of a function template specialization, the nontemplate function would always be selected because it‘s always considered to be a better match than a template. If you‘re writing a function template, prefer to write it as a single function template that should never be specialized or overloaded, and implement the function template entirely in terms of a class template. This is the proverbial level of indirection that steers you well clear of the limitations and dark corners of function templates. This way, programmers using your template will be able to partially specialize and explicitly specialize the class template to their heart‘s content without affecting the expected operation of the function template. This avoids both the limitation that function templates can‘t be partially specialized, and the sometimes surprising effect that function template specializations don‘t overload. Problem solved. If you‘re using someone else‘s plain old function template (one that‘s not implemented in terms of a class template as suggested above), and you want to write your own special-case version that should participate in overloading, don‘t make it a specialization; just make it an overloaded function with the same signature. AcknowledgmentsThanks to Peter Dimov and Dave Abrahams for prompting me to write about this topic and offering the key example, and to John Spicer for helping me get my head around the rationale for why things are the way they are. Notes1. In standardese, a full specialization is called an "explicit specialization." 2. There is some discussion going on within the committee about potentially allowing function template partial specialization in the next version of the C++ standard, whose work is just getting under way. |
Why Not Specialize Function Templates?
时间: 2024-11-03 21:01:00
Why Not Specialize Function Templates?的相关文章
C++ Core Guidelines
C++ Core Guidelines September 9, 2015 Editors: Bjarne Stroustrup Herb Sutter This document is a very early draft. It is inkorrekt, incompleat, and pµøoorly formatted. Had it been an open source (code) project, this would have been release 0.6. Copy
为什么不要特化函数模版?
/* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm;
templates(1.1)
为什么使用 Templates? C++ 要求我们使用各种特定类型(specific types)来声明变量.函数和其它各种实体(entities): 然而,很多用以处理「不同类型之数据」的程序代码看起来都差不多.特别是当你实作算法(像是quicksort),或实作如 linked-list 或 binary tree 之类的数据结构时,除了所处理的类型不同,程序代码实际上是一样的.如果你使用的编程语言并没有针对这个问题支持某种特殊的语言特性,那么你有数种退而次之的选择: 1. 针对每一种类型写
effective c++条款26-31“class and function的实现”整理
一.类的实现面临的问题: 太快定义变量可能造成效率上的拖延:过度使用转型(casts)可能导致代码变慢又难维护,又招来微妙难解的错误:返回对象"内部数据之号码牌(handls)"可能会破坏封装并留给客户虚吊号码牌:为考虑异常带来的冲击则可能导致资源泄漏和数据败坏:过度热心地inlining可能引起代码膨胀:过度耦合则可能导致让人不满意的冗长建置时间. 二.条款26:尽可能延后变量定义式的出现时间 有些对象,你可能过早的定义它,而在代码执行的过程中发生了导常,造成了开始定义的对象并没有被
C++ 高级篇(一)—— 模板(Templates)
模板(Templates)是ANSI-C++ 标准中新引入的概念.如果你使用的 C++ 编译器不符合这个标准,则你很可能不能使用模板. 函数模板( Function templates) 模板(Templates)使得我们可以生成通用的函数,这些函数能够接受任意数据类型的参数,可返回任意类型的值,而不需要对所有可能的数据类型进行函数重载.这在一定程度上实现了宏(macro)的作用.它们的原型定义可以是下面两种中的任何一个: template <class identifier> functio
templates(1.2)
max.hpp /* The following code example is taken from the book * "C++ Templates - The Complete Guide" * by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002 * * (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002. * Permissi
<;Effective C++>;读书摘要--Templates and Generic Programming<;一>;
1.The initial motivation for C++ templates was straightforward: to make it possible to create type-safe containers like vector, list, and map. Ultimately, it was discovered that the C++ template mechanism is itself Turing-complete: it can be used to
templates(2.1)
Class Templates 类别模板 就像上一章所说的 functions 那样,classes 也可以针对一或多个类型被参数化.用来管理「各种 不同类型的元素」的 container classes(容器类别)就是典型例子.运用 class templates 你可以实 作出可包容各种类型的 container class.本章将以一个 stack class 作为 class templates /* The following code example is taken from th
C++11 : variadic templates(可变参数模板)
Introduction: Before the possibilities of the new C++ language standard, C++11, the use of templates was quite limited when it came to implementing for instance function objects (functors) & tuple facilities. Implementing these sort of things using e