《Effective Modern C++》示例中索引运算符前使用std::forward的原因
不过我们需要更新该模板的实现,以符合第25条提出的对万能引用应用std::forward的建议:
template<typename Container, typename Index> decltype(auto) authAndAccess(Container&& c, Index i) { authenticateUser(); return std::forward<Container>(c)[i]; }
问题1:调用索引运算符时使用std::forward能带来什么收益?
这里的收益核心是保障泛型代码的通用性和正确性,不是针对所有容器都立刻能看到效果,而是适配所有符合语法规则的容器实现:
- C++允许类的成员函数加引用限定符,
operator[]作为成员函数也可以针对左值实例、右值实例提供不同的重载,返回不同的值类别。比如下面的自定义容器:class CustomContainer { private: std::vector<int> data = {1,2,3}; public: // 左值容器调用时返回左值引用 int& operator[](size_t idx) & { return data[idx]; } // 右值容器调用时返回右值引用 int&& operator[](size_t idx) && { return std::move(data[idx]); } }; - 如果你不对
c做std::forward,不管传入的c原始是左值还是右值,c作为函数内的左值变量,永远只会调用左值版本的operator[],直接丢失了右值容器的重载匹配能力。只有用std::forward<Container>(c)保留c的原始值类别,才能正确匹配到对应重载。 - 配合返回值的
decltype(auto),可以把operator[]返回的值类别原封不动传递给调用方:如果传入的是右值容器且operator[]返回右值引用,调用方就可以直接对返回值做移动构造,避免拷贝开销。
问题2:为什么测试代码里用了std::forward还是没有返回右值引用?
你的测试代码没有看到效果,是因为你用的std::vector的operator[]没有提供带右值引用限定的重载。标准委员会没有为std::vector加这个重载,核心是为了避免悬空引用的安全问题(比如临时vector返回的元素引用很容易在vector销毁后变成野引用),但这不代表std::forward在这里没用:
- 你把测试代码里的
std::vector<int>替换成上面的CustomContainer,就能看到明显区别:
运行后会打印:template<typename T> void f2(T && param) { f1(param[0]); // 永远调用f1(int &) f1(std::forward<T>(param)[0]); // 传入右值CustomContainer时调用f1(int &&) } int main() { f2(CustomContainer{}); // 传入右值自定义容器 return 0; }f1 called for int & f1 called for int && - 你的测试结论只适用于
std::vector这类没有右值版本operator[]的容器,而authAndAccess是通用模板,需要兼容所有可能的容器实现,包括标准库后续新增的容器、业务代码里的自定义容器,因此必须加std::forward保证语义正确。
内容的提问来源于stack exchange,提问作者not_that_guy123
相关产品推荐
相关产品推荐

