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

Cha*_*eon 4 c++ forwarding-reference

当我想要使用函数参数复制语义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;
}
Run Code Online (Sandbox Code Playgroud)

编辑: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;
}
Run Code Online (Sandbox Code Playgroud)

另一个始终基于user17732522答案的解决方案是:

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

template<typename T>
auto operator-(const T &a);
Run Code Online (Sandbox Code Playgroud)

use*_*522 6

您只需const从 中删除const T&

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

这里的问题是T&&推断TU, not const U,因为ain mainisn't const。在重载解析中,绑定到U&而不是被认为更好。const U&

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

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

a然而,这确实会导致函数中的结果不被const限定。您可以const通过申请从中获取参考std::as_const

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