我想移动语义,但我得到了一个隐藏复制语义的通用参考

I want to move semantics but I get a universal reference which hides copy semantics

当我想使用函数参数 const T& 复制语义或使用函数参数 T&& 移动语义时,如何处理通用引用。后者隐藏了第一个

后面是带有代数向量运算符的示例代码。

#include <array>
#include <iostream>


template<typename T>
void neg(T &a) { for (auto &i : a) i = -i; }

// object 'a' remains available
template<typename T>
auto operator-(const T &a) { std::cout << "1\r\n"; T b = a; neg(b); return b; }

// object 'a' terminates its life
template<typename T>
auto operator-(T &&a) { std::cout << "2\r\n"; neg(a); return std::move(a); }

// make an rvalue
template<typename T1, typename T2>
auto operator+(const T1 &a, const T2 &b) { return a; }


int main()
{
    std::array<int, 4> a;
    auto d = -(a+a);        // outputs 2 : correct
    auto e = -a;            // outputs 2 : I want 1
    auto f = -std::move(a); // outputs 2 : correct
    return 0;
}

编辑: user17732522 提出的解决方案之一(请给他投票)。

template<typename T>
auto operator-(T &&a)
{
    std::cout << "2\r\n"; neg(a); return std::move(a);
}
template<typename T>
auto operator-(T &a)
{
    std::cout << "1\r\n";
    std::remove_cvref_t<T> b = a;
    neg(b); return b;
}

int main()
{
    std::array<int, 4> a;
    auto d = -(a+a);        // now outputs 2 : correct
    auto e = -a;            // now outputs 1 : correct
    auto f = -std::move(a); // now outputs 2 : correct
    const std::array<int, 4> b{1,2,3,4};
    d = -(b+b);     // now outputs 2 : correct
    e = -b;         // now outputs 1 : correct
    return 0;
}

另一种解决方案,始终基于 user17732522 答案,是这样的:

template<typename T>
requires(!std::is_reference_v<T>)
auto operator-(T &&a);

template<typename T>
auto operator-(const T &a);

您只需从 const T& 中删除 const

using U = std::array<int, 4>;:

这里的问题是 T&&T 推断为 U,而不是 const U,因为 main 中的 a 不是const。并且绑定到 U& 而不是 const U& 在重载决议中被认为更好。

如果您从 const T& 中删除 const,那么两个候选人在推导后都会有一个函数参数 U&,因此基于此两者都不会更好。

然而,在函数模板的部分排序中,T& 模板将胜过 T&&,因此如果函数参数是左值,将选择前者。

然而,这确实会导致函数中的 a 不会被 const 限定。您可以通过应用 std::as_const.

从中获取 const 引用

或者,您可以使用 requires 子句或 std::enable_ifT 上的 T&& 模板限制为 non-reference 类型。或者您可以使用单个函数模板,并在其主体中根据 if constexpr 的引用类型决定如何操作。相关类型特征:std::is_lvalue_reference_v<T>std::is_reference_v<T>std::is_lvalue_reference_v<decltype(a)>.