我正在阅读Scott Meyers的书中有关现代C ++的decltype
和rvalue参考文献。我有以下代码
template <typename container, typename index>
decltype(auto) authAndAccess(container&& c, index i) {
std::cout << "auth and Access c type: " << typeid(c[i]).name() << std::endl;
std::cout << "auth and Access c type expecting reference: " << typeid(std::forward<container>(c)[i]).name() << std::endl;
return std::forward<container>(c)[i];
}
deque<int> makeStringDeque() {
deque<int> dqContainer = { 1,2,3,4,5 };
return dqContainer;
}
现在在主要功能我有以下
deque<int> dqContainer = { 1,2,3,4,5 };
std::cout << "Value returned by container: " << authAndAccess(dqContainer, 4) << std::endl;
authAndAccess(deque<int>{1, 2, 3, 4, 5}, 4) = 10;
std::cout << "Value returned by container and 5th element after copying: " << dqContainer[4] << endl;
我的问题是authAndAccess
函数采用rvalue参数,所以容器是临时的,返回的对象是临时引用元素。但为什么输出显示为int
为typeid
forware,我期待int &
。我知道typeid的名称功能不准确,但是为什么它不会崩溃,因为我们正在返回临时元素引用。
我的问题是
authAndAccess
函数采用rvalue参数,所以容器是临时的,返回的对象是临时引用元素。
authAndAccess
采用Scott称之为Universal Reference的方式,现在称为转发引用,因此它可以接受lvalue和rvalue参数。当你传递一个左值时,你得到一个左值参考,当你传递一个右值时,你得到一个左值参考。 std::forward<container>
做同样的事情。如果container
是左值,则得到左值,对于右值,你得到左值。
这意味着authAndAccess(dqContainer, 4)
很好,因为你返回对dqContainer
中仍然存在的对象的引用。在authAndAccess(makeStringDeque(), 4)
你会认为你有未定义的行为,因为makeStringDeque()
是一个临时的,你正在返回它的引用,但由于你不保留它,没有UB,因为引用将有效直到完整表达式结束。
但为什么输出显示为
int
为typeid
转发,我期待int &
。
typeid
不会告诉你是否有参考。你可以在这个最小的例子中看到
int main(int argc, char* argv[])
{
int a = 5;
int & b = a;
std::cout << typeid(b).name();
}
输出i
。如果你想获得类型,你可以使用声明但未定义的类模板并给它类型,你会收到一条错误消息,告诉你实际的类型是什么。如果我们将上面的代码更改为
template<typename T>
struct type;
int main(int argc, char* argv[])
{
int a = 5;
int & b = a;
type<decltype(b)>{};
}
我们会得到一个错误
main.cpp:14:5: error: implicit instantiation of undefined template 'type<int &>'
type<decltype(b)>{};
正如你所看到的,它推断出类型为int &
。如果我们在你的代码中这样做,它也会给int &
,因为那是c[i]
的返回类型。