第1章 函数模板:1.6 但是,我们不应该…?

1.6 But, Shouldn’t We …?

1.6 但是,难道我们不应该…?

Probably, even these simple function template examples might raise further questions. Three questions are probably so common that we should discuss them here briefly.

也许,即使这些很简单的函数模板示例也可能引发其他问题。我们会在这里简要讨讨3个可能很常见的问题。

1.6.1 Pass by Value or by Reference?

1.6.1 传值或传引用?

You might wonder, why we in general declare the functions to pass the arguments by value instead of using references. In general, passing by reference is recommended for types other than cheap simple types (such as fundamental types or std::string_view), because no unnecessary copies are created.

你可能想知道,为什么我们通常使用值而不是引用传参方式来声明这些函数?一般来说,建议通过引用传递简单类型(如基本类型或std::string_view)以外的类型,因为不会创建不必要的副本。

However, for a couple of reasons, passing by value in general is often better:

但是,出于以下几个原因,按值传递参数通常会更好:

    • The syntax is simple.

    简单很语法

    • Compilers optimize better.

    编译器优化得更好

    • Move semantics often makes copies cheap.

    移动语义通常会使用拷贝代价更小

    • And sometimes there is no copy or move at all.

    有时根本没有拷贝或移动

In addition, for templates, specific aspects come into play:

此外,对于模板,特定方面也起作用:

    • A template might be used for both simple and complex types, so choosing the approach for complex types might be counter-productive for simple types.

    模板可能同时用于简单类型和复杂类型,因此为复杂类型选择的方式可能对简单类型将产生相反的效果。

    • As a caller you can often still decide to pass arguments by reference, using std::ref() and std::cref() (see Section 7.3 on page 112).

    作为调用者,你通常仍然可以决定通过引用传递参数,使用std::ref()和std::cref()(参见第112页7.3节)

    • Although passing string literals or raw arrays always can become a problem, passing them by reference often is considered to become the bigger problem. All this will be discussed in detail in Chapter 7. For the moment inside the book we will usually pass arguments by value unless some functionality is only possible when using references

    尽管传递字符串文本和原生数组总会成一个问题,但通过引用传递它们通常被认为是一个更大的问题。所有这些将在第7章中详细讨论。目前在本书中,我们通常会按值传递参数,除非某些功能只有在使用引用时才可以实现。

1.6.2 Why Not inline?

1.6.2 为什么不使用内联呢?

In general, function templates don’t have to be declared with inline. Unlike ordinary noninline functions, we can define noninline function templates in a header file and include this header file in multiple translation units.

通常情况下,函数模板不一定要用inline来声明。与普通非内联函数不同,我们可以在头文件中定义非内联的函数模板,并将此文件包含在多个转换单元中。

The only exception to this rule are full specializations of templates for specific types, so that the resulting code is no longer generic (all template parameters are

defined). See Section 9.2 on page 140 for more details.

该规则唯一例外的是针对特定类型的模板完全特化,因此生成的代码不再是泛型(定义了所有的模板参数)。有关更多详细信息,请参考第140页的9.2节。

From a strict language definition perspective, inline only means that adefinition of a function can appear multiple times in a program. However, it is also meant as a hint to the compiler that calls to that function should be “expanded inline”: Doing so can produce more efficient code for certain cases, but it can also make the code less efficient for many other cases. Nowadays, compilers usually are better at deciding this without the hint implied by the inline keyword. However, compilers still account for the presence of inline in that decision.

从严格的语言定义角度来看,内联只意味着函数的定义可以在程序中多次出现。但是,它也意味着它提示编译器应该对该函数的调用进行“内联扩展”:这样做可以在某些情况下产生更高效的代码,但是在许多其他情况下也可能使代码效率降低。现在,编译器通常更擅长在没有inline关键字的提示下决定这一点。但编译器在决策时仍然会考虑inline的存在。

1.6.3 Why Not constexpr?

1.6.3 为什么不使用constexpr?

Since C++11, you can use constexpr to provide the ability to use code to compute some values at compile time. For a lot of templates this makes sense.

从C++11开始,你可以使用constexpr以提供在编译器计算某些值的能力。对于许多模板来说,这是有意义的。

For example, to be able to use the maximum function at compile time, you have to declare it as follows:

例如,为了在编译期使用max函数,必须按如下方式声明它:

template<typename T1, typename T2>
constexpr auto max(T1 a, T2 b)
{
    return b < a ? a : b;
}

With this, you can use the maximum function template in places with compile-time context, such as when declaring the size of a raw array:

这样,你就可以在具有编译期的语境中使用max函数模板,例如在声明原生数组的大小时:

int a[::max(sizeof(char),1000u)];

or the size of a std::array<>:  

或者std::array<>的大小:

std::array<std::string, ::max(sizeof(char),1000u)> arr;

Note that we pass 1000 as unsigned int to avoid warnings about comparing a signed with an unsigned value inside the template.

请注意,我们将1000作为unsigned int传入,以避免在模板中比较有符号值和无符号值时出现警告。

Section 8.2 on page 125 will discuss other examples of using constexpr. However, to keep our focus on the fundamentals, we usually will skip constexpr when discussing other template features.

第125页的8.2节将讨论使用constexpr的其他示例。但是,为了保持对基础知识的关注,在讨论其他模板特性时,通常会忽略constexpr。

1.7 Summary

1.7 总结

    • Function templates define a family of functions for different template arguments.

    函数模板为不同的模板参数定义了一系列的函数。

    • When you pass arguments to function parameters depending on template parameters, function templates deduce the template parameters to be instantiated for the corresponding parameter types.

    当你传递模板实参的时候,可以根据实参的类型来对函数模板进行实例化。

    • You can explicitly qualify the leading template parameters.

    可以显式限定前导模板参数的类型

    • You can define default arguments for template parameters. These may refer to previous template parameters and be followed by parameters not having default arguments.

    可以为模板参数定义默认参数。这些参数可能引用以前的模板参数,后面跟着没有默认参数的参数。

    • You can overload function templates.

    可以重载函数模板

    • When overloading function templates with other function templates, you should ensure that only one of them matches for any call.

    当与其他函数模板一起进行重载函数模板时,任何的函数调用都应确保其中只有一个模板与之匹配。

    • When you overload function templates, limit your changes to specifying template parameters explicitly.

    重载函数模板时,把你的改变限制在:显式指定模板参数。

    • Ensure the compiler sees all overloaded versions of function templates before you call them.

    函数调用之前,请确保编译器可以看到函数模板的所有重载版本。

1 Note that the max() template according to [Stepanov Notes] intentionally returns “b < a ? a : b” instead of “a < b ? b : a” to ensure that the function behaves correctly even if the two values are equivalent but not equal.

1 注意,根据Stepanov的注解,max()模板特地返回”b<a?a:b”而不是”a<b?b:a”,是为了确保即使两个值等价但不相等时,函数也能正确运行。

2 Before C++17, type T also had to be copyable to be able to pass in arguments, but since C++17 you can pass temporaries (rvalues, see Appendix B) even if neither a copy nor a move constructor is valid.

2 在C++17之前,为了能够传递参数,T类型必须是可拷贝的。但从C++17开始,即使拷贝和移动构造函数都是无效的,也可以传递临时对象(右值,见附录B)

3 For example, if one argument type is defined in namespace std (such as std::string), according to the lookup rules of C++, both the global and the max() template in std are found (see Appendix C).

3 例如,如果使用命名空间std中定义的一个参数类型(如std::string),根据C++查找规则,将同时在全局和std命名空间中找到max()函数模板(见附录C)

4 A “one-entity-fits-all” alternative is conceivable but not used in practice (it would be less efficient at run time). All language rules are based on the principle that different entities are generated for different template arguments.

4 可以构想一个“1个实体适应所有”的替代方案,但在实践中并没有这样做(在运行期效率会降低)。所有语言规则均基于以下原则:为不同模板参数生成不同实体。

5 The terms instance and instantiate are used in a different context in object oriented programming—namely, for a concrete object of a class. However, because this book is about templates, we use this term for the “use” of templates unless otherwise specified.

5 术语“实例”和“实例化”用于面向对象编程的不同语境中——即类的具体对象。但是,由于本书是关于模板的,除非另有说明,我们将使用这个术语来表示“使用”模板。

6 For example, The Visual C++ compiler in some versions (such as Visual Studio 2013 and 2015) allow undeclared names that don’t depend on template parameters and even some syntax flaws (such as a missing semicolon).

6 例如,某些版本的Visual C++编译器(如Visual Studio 2013和2015)允许使用不依赖于模板参数的未声明名称,甚至不依赖某些语法缺陷(如缺少分号)。

7 Deduction can be seen as part of overload resolution—a process that is not based on selection of return types either. The sole exception is the return type of conversion operator members.

7 推导可以被看成是重载解析的一部分。重载解析是一个不依赖于返回类型的选择过程,唯一例外的是类型转换运算符的返回类型。

8 In C++, the return type also cannot be deduced from the context in which the caller uses the call.

8 在C++中,返回类型也不能从调用者使用的调用语境中推导出来。

9 Prior to C++11, default template arguments were only permitted in class templates, due to a historical glitch in the development of function templates.

9 在C++11之前,默认模板参数中允许在类模板中使用,因为函数模板在开发中存在历史缺陷。

10 Again, in C++11 you had to use typename std::decay<…>::type instead of std::decay_t<…> (see Section 2.8 on page 40).

10 同样,在C++11中你必须使用typename std::decay<…>::type来替代std::decay_t<…>(见第40页2.8节)

11 In general, a conforming compiler isn’t even permitted to reject this code.

11 一般遵循标准的编译器不应当拒绝这些代码。

猜你喜欢

转载自www.cnblogs.com/5iedu/p/12696741.html