参数类型推导、引用和右值
考虑这样一种情况:函数模板需要转发参数,同时保持其左值性(以防它是非常量左值),但其本身不知道参数实际是什么,如下所示:
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
可以处理右值(无需调用显式模板参数)需要是一个 forward(const T&)
重载,即使它的主体是完全重复的。
有什么办法可以避免这种重复吗?
Consider the situation where a function template needs to forward an argument while keeping it's lvalue-ness in case it's a non-const lvalue, but is itself agnostic to what the argument actually is, as in:
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);
}
When x
is an rvalue, instead of T
being deduced to a constant type, it gives an error:
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
It seems that for forward
to handle rvalues (without calling for explicit template arguments) there needs to be an forward(const T&)
overload, even though it's body would be an exact duplicate.
Is there any way to avoid this duplication?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是一个已知问题,也是 C++0x 中右值引用的目的。该问题在 C++03 中没有通用解决方案。
发生这种情况有一些古老的历史原因,但毫无意义。我记得曾经问过一次,答案让我非常沮丧。
This is a known problem and the purpose of rvalue references in C++0x. The problem has no generic solution in C++03.
There is some archaic historical reason why this occurs that is very pointless. I remember asking once and the answer depressed me greatly.
一般来说,模板的这个令人讨厌的问题应该需要重复,因为变量是否为 const、或者是否为引用的语义是非常不同的。
C++11 对此的解决方案是“decltype”,但这是一个坏主意,因为它所做的只是复合且已经损坏的类型系统。
无论标准或委员会怎么说,“const int”都不是也永远不会是一种类型。 “int&”也不会永远是一种类型。因此,模板中的类型参数永远不允许绑定到此类非类型,值得庆幸的是,事实就是如此。不幸的是,您仍然可以明确强制这种无原则的替换。
有一些愚蠢的规则试图“修复”这个问题,例如“const const int”减少为“const int”,我什至不确定如果你得到“int & &”会发生什么:甚至记住标准不计算“int&”作为一种类型,有一个“int lvalue”类型,但这是不同的:
这个问题的正确解决方案实际上非常简单:一切都是可变对象。扔掉“const”(它没那么有用)并扔掉引用、左值和右值。很明显,所有类类型都是可寻址的,无论是否是右值(“this”指针是地址)。委员会徒劳地试图禁止对右值进行赋值和寻址。寻址案例有效,但很容易通过简单的强制转换来逃脱。赋值情况根本不起作用(因为赋值是一个成员函数并且右值是非常量,所以您始终可以分配给类类型的右值)。
无论如何,模板元编程人群都有“decltype”,通过它,您可以找到包含任何“const”和“&”的声明的编码。位,然后您可以使用各种库运算符分解该编码。以前无法做到这一点,因为该信息实际上不是类型信息(“ref”实际上是存储分配信息)。
In general, this nasty problem with templates ought to require duplication because the semantics where a variable is const or not, or a reference or not, are quite distinct.
The C++11 solution to this is "decltype" but it is a bad idea because all it does is compound and already broken type system.
No matter what the Standard or the Committee says, "const int" is not and will never be a type. Nor will "int&" ever be a type. Therefore a type parameter in a template should never be allowed to bind to such non-types, and thankfully for deduction this is the case. Unfortunately you can still explicitly force this unprincipled substitution.
There are some idiotic rules which try to "fix" this problem, such as "const const int" reducing to "const int", I'm not even sure what happens if you get "int & &": remember even the Standard doesn't count "int&" as a type, there is a "int lvalue" type but that's distinct:
The right solution to this problem is actually quite simple: everything is a mutable object. Throw out "const" (it isn't that useful) and throw away references, lvalues and rvalues. It's quite clear that all class types are addressable, rvalue or not (the "this" pointer is the address). There was a vain attempt by the committee to prohibit assigning to and addressing rvalues.. the addressing case works but is easily escaped with a trivial cast. The assignment case doesn't work at all (because assignment is a member function and rvalues are non-const, you can always assign to a class typed rvalue).
Anyhow the template meta-programming crowd have "decltype" and with that you can find the encoding of a declaration including any "const" and "&" bits and then you can decompose that encoding using various library operators. This couldn't be done before because that information is not actually type information ("ref" is actually storage allocation information).
但是 x 永远不是右值,因为名称是左值。
C++0x 中有一种方法:
由于引用折叠规则,表达式
std::forward(x)
与您自己的前向函数的参数具有相同的值类别。But
x
is never an rvalue, because names are lvalues.There is a way in C++0x:
Thanks to reference collapsing rules, the expression
std::forward<T>(x)
is of the same value-category as the argument to your own forward function.假设有 k 个参数,据我所知,C++03 中唯一的“解决方案”是手动编写 2^k 个转发函数,采用
&
和const& 的每种可能的组合。
参数。为了说明这一点,假设target()
实际上有 2 个参数。然后您将需要:显然,对于大 k 来说,这变得非常笨拙,因此正如其他答案所提到的,C++0x 中的右值引用。
Assuming there are k arguments, as I understand it the only "solution" in C++03 is to manually write out 2^k forwarding functions taking every possible combination of
&
andconst&
parameters. For illustration, imagine thattarget()
actually took 2 parameters. You would then need:Obviously this gets very unwieldy for large k, hence rvalue references in C++0x as other answers have mentioned.