6

考虑一个函数模板需要转发一个参数同时保持它的左值性以防它是一个非常量左值的情况,但它本身与参数的实际情况无关,如下所示:

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&)重载,即使它的主体将是一个完全相同的副本。

有没有办法避免这种重复?

4

4 回答 4

5

这是一个已知问题,也是 C++0x 中右值引用的目的。该问题在 C++03 中没有通用解决方案。

发生这种情况有一些古老的历史原因,这是非常没有意义的。我记得问过一次,答案让我很沮丧。

于 2011-01-02T13:40:11.970 回答
2

什么时候x是右值

Butx永远不是右值,因为名称是左值。

有没有办法避免这种重复?

C++0x中有一种方法:

#include <utility>

template <typename T>
void forward(T&& x)   // note the two ampersands
{
    target(std::forward<T>(x));
}

由于引用折叠规则,表达式std::forward<T>(x)与您自己的 forward 函数的参数具有相同的值类别。

于 2011-01-02T13:43:11.627 回答
2

一般来说,模板的这个讨厌的问题应该需要重复,因为变量是否为 const 或不是引用的语义是完全不同的。

对此的 C++11 解决方案是“decltype”,但这是一个坏主意,因为它所做的只是复合且已经损坏的类型系统。

无论标准或委员会怎么说,“const int”都不是,也永远不会是一种类型。“int&”也不会是一种类型。因此,不应允许模板中的类型参数绑定到此类非类型,谢天谢地,情况就是这样。不幸的是,您仍然可以明确地强制这种无原则的替换。

有一些愚蠢的规则试图“解决”这个问题,例如“const const int”减少到“const int”,我什至不确定如果你得到“int & &”会发生什么:记住即使标准没有'不把“int&”算作一种类型,有一个“int lvalue”类型,但这是不同的:

int x;       // type is lvalue int
int &y = x;  // type is lvalue int

这个问题的正确解决方案其实很简单:一切都是可变对象。扔掉“const”(它没那么有用)并扔掉引用、左值和右值。很明显,所有类类型都是可寻址的,无论是否右值(“this”指针就是地址)。委员会试图禁止分配和寻址右值是徒劳的。寻址案例有效,但很容易用微不足道的演员表逃脱。赋值情况根本不起作用(因为赋值是一个成员函数并且右值是非常量的,你总是可以赋值给一个类类型的右值)。

无论如何,模板元编程人群具有“decltype”,并且您可以找到包含任何“const”和“&”位的声明的编码,然后您可以使用各种库运算符分解该编码。以前无法做到这一点,因为该信息实际上不是类型信息(“ref”实际上是存储分配信息)。

于 2011-01-02T14:39:57.547 回答
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 中的右值引用。

于 2011-01-02T14:33:57.067 回答