【发布时间】:2019-03-06 23:32:54
【问题描述】:
假设你有以下一对函数:
void f(const int&) {
// Do something, making a copy of the argument.
}
void f(int&&) {
// Do the same thing, but moving the argument.
}
它们是相当多余的——函数之间的唯一区别是它们是复制还是移动它们的参数。当然,我们可以通过将其重写为单个模板函数来做得更好:
template<typename T>
void g(T&&) {
// Do something, possibly using std::forward to copy or move the argument.
}
这行得通,并且是实践中常用的成语。但是模板可能被实例化为三个函数,从我们上面的两个函数开始。我们可以使用以下代码来验证这种情况:
#include <iostream>
template<typename T> constexpr char *type = nullptr;
template<> constexpr const char *type<int&> = "int&";
template<> constexpr const char *type<const int&> = "const int&";
template<> constexpr const char *type<int> = "int";
template<typename T>
void g(T&&) {
std::cout << reinterpret_cast<void*>(&g<T>)
<< " = &g<" << type<T> << ">" << std::endl;
}
int main() {
int i = 0;
const int& cr = 0;
g(i);
g(cr);
g(0);
return 0;
}
/*
Prints:
0x100f45080 = &g<int&>
0x100f45100 = &g<const int&>
0x100f45180 = &g<int>
*/
这为T = int& 的情况添加了第三个函数,这是我们在上面使用非模板函数f 时没有的。在这种情况下,我们实际上并不需要这个函数的非 const l 值引用版本——假设 f 足以满足我们最初的需求——这会增加我们代码的大小,尤其是在我们有很多模板函数的情况下写成这样互相呼唤。
有没有办法编写上面的函数g,以便在我们的示例代码中调用g(i) 时,编译器会自动推导出T = const int&?即,我们不必手动编写 g<const int&>(i) 仍能获得所需行为的方式。
【问题讨论】:
-
函数有可能被完全内联,移除了额外的函数。
-
@Jarod42 同意此处示例中的函数大小。尽管取决于
f和g的大小以及它们自己调用的更多函数,但它开始变得更加模棱两可。以前见过这种情况没有被内联。 -
如果您担心相同的功能,链接器通常可以选择合并它们。 (如黄金/lld的
--icf) -
实际上每个值类型有 8 种可能的实例化:cv-qualifiers 使它成为 2(for constness)x 2(for volatileness),r/l-valueness 将它乘以另一个 2。
标签: c++ c++11 templates perfect-forwarding template-argument-deduction