考虑一下函数模板需要转发参数的情况,同时保持它的左值,以防它是非常量左值,但它本身与参数实际上是无关的,如:
template <typename T>
void target(T&) {
cout << "non-const lvalue";
}
template <typename T>
void target(const T&) {
cout << "const lvalue or rvalue";
}
template <typename T>
void forward(T& x) {
target(x);
}
如果x
是左值,而不是T
被推导为常量类型,则会出错:
int x = 0;
const int y = 0;
forward(x); // T = int
forward(y); // T = const int
forward(0); // Hopefully, T = const int, but actually an error
forward<const int>(0); // Works, T = const int
似乎forward
处理rvalues(不调用显式模板参数)需要forward(const T&)
重载,即使它的正文是完全重复的。
有没有办法避免这种重复?
答案 0 :(得分:5)
这是一个已知问题,也是C ++ 0x中rvalue引用的目的。这个问题在C ++ 03中没有通用的解决方案。
有一些古老的历史原因,为什么会发生这种情况,这是非常无意义的。我记得曾经问过一次,答案让我非常沮丧。
答案 1 :(得分:2)
当
x
是右值时
但x
永远不是右值,因为名字是左值。
有没有办法避免这种重复?
C ++ 0x中有一种方法:
#include <utility>
template <typename T>
void forward(T&& x) // note the two ampersands
{
target(std::forward<T>(x));
}
由于引用了折叠规则,表达式std::forward<T>(x)
与您自己的转发函数的参数具有相同的值类别。
答案 2 :(得分:2)
一般来说,模板应该的这个讨厌的问题需要重复,因为变量是const或不是或者引用的语义是完全不同的。
C ++ 11对此的解决方案是“decltype”,但这是一个坏主意,因为它所做的只是复合和已经破坏的类型系统。
无论标准或委员会说什么,“const int”都不是,也永远不会是一种类型。 “int&amp;”也不会永远是一个类型。因此,永远不允许模板中的类型参数绑定到这样的非类型,并且幸运的是扣除这种情况。不幸的是,您仍然可以明确强制进行无原则的替换。
有一些愚蠢的规则试图“修复”这个问题,例如“const const int”减少到“const int”,我甚至不确定如果你得到“int&amp;&amp;”会发生什么:记住即使标准也不计算“int&amp;”作为一种类型,有一个“int lvalue”类型,但它是不同的:
int x; // type is lvalue int
int &y = x; // type is lvalue int
解决这个问题的正确方法实际上很简单:一切都是可变对象。抛出“const”(它没那么有用)并抛弃引用,左值和右值。很明显,所有类类型都是可寻址的,rvalue或不是(“this”指针是地址)。委员会徒劳地试图禁止分配和解决rvalues ..寻址案件有效但很容易通过琐碎的演员逃脱。赋值情况根本不起作用(因为赋值是一个成员函数,而rvalues是非const的,你总是可以赋值给一个类型的rvalue)。
无论如何模板元编程人群都有“decltype”,你可以找到包含任何“const”和“&amp;”的声明的编码。比特然后你可以使用各种库运算符分解该编码。之前无法做到这一点,因为该信息实际上不是类型信息(“ref”实际上是存储分配信息)。
答案 3 :(得分:0)
假设有k个参数,据我所知,C ++ 03中唯一的“解决方案”是手动写出2 ^ k个转发函数,并采用&
和const&
个参数的每种可能组合。为了说明,假设target()
实际上有2个参数。然后你需要:
template <typename T>
void forward2(T& x, T& y) {
target(x, y);
}
template <typename T>
void forward2(T& x, T const& y) {
target(x, y);
}
template <typename T>
void forward2(T const& x, T& y) {
target(x, y);
}
template <typename T>
void forward2(T const& x, T const& y) {
target(x, y);
}
显然这对于大k来说非常笨重,因此其他答案提到的C ++ 0x中的右值引用。