什么时候&&是指'转发参考'?

时间:2016-12-20 19:44:07

标签: c++ parameter-passing

编译时,以下代码会导致此错误:

  

'Container :: Wrapper :: Wrapper(S)':已经是成员函数   定义或声明

编译器是否认为S&&的构造函数中的Wrapper是转发引用?

template<typename T>
struct Container
{
    template<class S>
    struct Wrapper {
        S obj;
        Wrapper(S&& obj) : obj(std::forward<S>(obj)){}
        Wrapper(const S& obj) : obj(obj){}
    };

    template<class S>
    void push_back(S&& obj) {
        void *storage = malloc(sizeof(Wrapper<S>));
        new (storage) Wrapper<S>(std::forward<S>(obj));
    }
};

struct Foobar{};

int main()
{
    Container<Foobar> cont;
    Foobar foobar;
    cont.push_back(foobar);
    return 0;
}

查看here的示例,我不明白我的工作方式有何不同:

template <class T, class Allocator = allocator<T> >
class vector {
public:
    ...
    void push_back(T&& x);       // fully specified parameter type ⇒ no type deduction;
    ...                          // && ≡ rvalue reference
};


修改 此问题的解决方案是修改push_back以从用于实例化Wrapper的类型中删除引用:

template<class S>
void push_back(S&& obj) {
    typedef std::remove_reference<S>::type U;
    void *storage = malloc(sizeof(Wrapper<U>));
    new (storage) Wrapper<U>(std::forward<S>(obj));
}

1 个答案:

答案 0 :(得分:2)

Wrapper的实施没有涉及它的通用引用。代码中唯一的通用引用是Container<>::push_back的参数。

当您致电cont.push_back(foobar);时,S的参数push_back推断为Foobar &

稍后您尝试使用Wrapper<S>实例化S == Foobar &。参考折叠规则规定在Wrapper的构造函数参数声明中S &&变为Foobar &const S &也变为Foobar &

这意味着您最终会得到两个具有相同签名的“重载”Wrapper::Wrapper构造函数。这就是您观察到的错误消息的原因。

如果尝试使用左值引用类型的模板参数实例化std::vector,则会遇到与push_back重载完全相同的问题。但是,有了这样的尝试,编译通常会因其他原因而失败,远远超过push_back重载。

更精简的例子如下所示

template <typename T> struct MyVector {
  void foo(T &&) {}
  void foo(const T &) {}
};

int main() {
  MyVector<int &> v;
}

并将产生相同的错误。

这里相当不明显的部分是const T & T = U &实际上变为U &而不是const U &。但情况确实如此。