为什么当T = int&时构造函数Message(const T& data)和Message(T&& data)冲突?

Why constructor Message(const T& data) is conflict with Message(T&& data) when T = int&?

template <typename T>
struct Message {
    T data;
    explicit Message(T&& data) : data(std::move(data)) {
        std::cout << "Move data" << std::endl;
    }

    explicit Message(const T& data) : data(data) {
        std::cout << "Copy data" << std::endl;
    }
};

template <typename T>
inline Message<T>*
makeMessage(T&& data) {
    return new Message<T>{std::forward<T>(data)};
}


int main() {
    const int a = 1024;
    auto *copy_msg = makeMessage(a);
}

有一个模板 class Message 有两个构造函数:Message(T&& data)Message(const T& data),当我调用 makeMessage(a).

error: multiple overloads of 'Message' instantiate to the same signature 'void (const int &&)'

explicit Message(const T& data) : data(data) {

之前的声明在这里

explicit Message(T&& data) : data(std::move(data)) {

但是,当我调用 make_message(1024)make_message(std::move(a)) 时它起作用了。

那么当 T = int& 时,为什么构造函数 Message(const T& data)Message(T&& data) 重复?

So why constructor Message(const T& data) is duplicate with Message(T&& data) when T = int&?

由于引用折叠规则。

没有引用引用之类的东西。当 T 是一个左值引用时——比方说 int &,那么在句法上 T & 似乎是一个 int & &。但是这种类型不存在。语言规则说在这种情况下 T & 会折叠成 int &.

同样,也没有常量引用这样的东西(不要与常量引用混淆,人们在说常量引用时有时指的是常量引用)。当 T 是一个左值引用时——比方说 int&,那么在句法上 T const(与 const T 相同)似乎是一个 int& const(不要与int const &,等同于const int &)。但是这种类型不存在。语言规则说在这种情况下 T const 会折叠成 int &.

C++11 引入了右值引用,这给我们带来了新的折叠规则。简而言之,"rvalue reference to rvalue reference" 折叠成右值引用,但 "lvalue reference to any reference" 和 "any reference to lvalue reference" 折叠成左值引用。这条规则是转发引用工作原理的一部分。

因此,给定 T = int &,它们声明相同的函数:

explicit Message(T&& data)      // type of argument is int &
explicit Message(const T& data) // type of argument is int &

奖励:对于非引用也有折叠规则:没有 const const 类型这样的东西。因此,给定 const T,其中 Tconst int,然后它会折叠成 const intvolatile.

也是如此