5

假设我们有一个 Box 类,如下所示:

struct Base {}

template<typename T>
struct Box : Base
{
    template<typename... Args>
    Box(Args&&... args)
        : t(forward<Args>(args)...)
    {}

    T t;
}

然后我们有一个函数 MakeBox:

template<typename X>
Base* MakeBox(X&& x)
{
    return new Box<???>(forward<X>(x));
}

类型X是从调用 MakeBox 时使用的参数推导出来的。

然后我们需要以某种方式从 X 计算出适当的“存储类型”参数 T。

我想如果我们只是天真地使用:

    return new Box<X>(forward<X>(x));

那么这将导致问题。

明明std::bindstd::function需要处理这些问题,他们是怎么做的呢?

无论如何在这里有std::decay帮助吗?

4

2 回答 2

3

如果我正确理解您想要实现的目标,那么您需要使用std::decay. 假设您提供 to 类型的对象SMakeBox()通用引用X&&将以这样的方式解析,以使函数参数成为类型S&S&&取决于您的参数是(分别)是左值还是右值。

为了实现这一点并且由于通用引用的 C++11 规则,在第一种情况下,模板参数将被推断为X=S&(这里X不能作为 的参数Box<>,因为您的成员变量必须是对象而不是对象参考),而在第二种情况下,它将被推断为X=S(这里X可以作为 的参数Box<>)。通过应用std::decay,您还将在将推导类型作为模板参数提供给 之前隐式应用于std::remove_reference推导类型,您将确保它始终相等且永远不会(请记住,这永远不会像这里那样推导,它将是或XBox<>XSS&XS&&SS&)。

#include <utility>
#include <type_traits>
#include <iostream>

using namespace std;

struct Base {};

template<typename T>
struct Box : Base
{
    template<typename... Args>
    Box(Args&&... args)
        : t(forward<Args>(args)...)
    {
    }

    T t;
};

template<typename X>
Base* MakeBox(X&& x)
{
    return new Box<typename decay<X>::type>(forward<X>(x));
}

struct S
{
    S() { cout << "Default constructor" << endl; }
    S(S const& s) { cout << "Copy constructor" << endl; }
    S(S&& s) { cout << "Move constructor" << endl; }
    ~S() { cout << "Destructor" << endl; }
};

S foo()
{
    S s;
    return s;
}

int main()
{
    S s;

    // Invoking with lvalue, will deduce X=S&, argument will be of type S&
    MakeBox(s);

    // Invoking with rvalue, will deduce X=S, argument will be of type S&&
    MakeBox(foo());

    return 0;
}

如果你有兴趣,这里是 Scott Meyers 的一个很好的课程,他解释了通用引用的行为:

Scott Meyers 谈通用参考

PS:这个答案已被编辑:我原来的答案建议使用std::remove_reference<>,但std::decay结果证明是一个更好的选择。感谢问题海报@Andrew Tomazos FathomlingCorps,他指出了这一点,以及@Mankarse,他首先在对原始问题的评论中提出了这个问题。

于 2012-12-30T22:18:52.870 回答
1

对于提供的示例(1),您可以执行您想要的操作并存储该值。对于其他情况(例如何时x是数组),您可以使用std::decay将其衰减为指针并将其存储。

于 2012-12-30T08:02:02.033 回答