如何使用 if constexpr 根据向量的类型将一个向量移动或分配给另一个向量?

T.L*_*T.L 15 c++ templates metaprogramming c++17

我想根据类型将 a 移动或分配std::vector<Scalar>给a 。std::vector<float>Scalar

\n

如果Scalarfloat则移动,否则复制。

\n

我试过这段代码

\n
#include <vector>\n\nusing Scalar = double;\n\nint main()\n{\n    std::vector<Scalar> x(10);\n    std::vector<float> y(10);\n\n    if constexpr(std::is_same<Scalar, float>::value)\n        y = std::move(x); // line 11 (does not compile)\n    else\n        y.assign(x.begin(), x.end());\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

但我得到

\n
main.cpp:11:24: error: no match for \xe2\x80\x98operator=\xe2\x80\x99 (operand types are \xe2\x80\x98std::vector<float>\xe2\x80\x99 and \xe2\x80\x98std::remove_reference<std::vector<double>&>::type\xe2\x80\x99 {aka \xe2\x80\x98std::vector<double>\xe2\x80\x99})\n   11 |         y = std::move(x);\n      |                        ^\n
Run Code Online (Sandbox Code Playgroud)\n

我认为由于在编译时std::is_same<Scalar, float>::value评估为,那么下面的行将不会被编译。false

\n

我使用编译它g++ -std=c++17 main.cpp -o exec

\n

如何根据类型移动x/y分配Scalar

\n

Tob*_*ght 15

因为main()不是模板,所以两边都if constexpr必须有效。要使用if constexpr这种方式,它需要位于模板中。

值得庆幸的是,一个好的模板使这个逻辑更可重用:

#include <vector>

template<typename T, typename U>
void move_or_copy(std::vector<T>& dest, std::vector<U>&& src)
{
    if constexpr(std::is_same_v<T,U>)
        dest = std::move(src);
    else
        dest.assign(src.begin(), src.end());
}



using Scalar = double;

int main()
{
    std::vector<Scalar> x(10);
    std::vector<float> y(10);

    move_or_copy(y, std::move(x));
}
Run Code Online (Sandbox Code Playgroud)

我们可以对模板函数进行更好的约束(允许任何集合作为源),但这至少应该提供正确的出发方向。

  • 是的,使用集合作为模板参数 `template &lt;typename Container1, typename Container2&gt;` 第二个参数将是真正的转发引用。目前,例如 `move_or_copy(y, y);` 是不可能的。 (2认同)

T.L*_*T.L 10

正如HolyBlackCat在评论中所指出的,以及 cppref 中所述:“在模板之外,会完全检查被丢弃的语句。” (https://en.cppreference.com/w/cpp/language/if)。

这是一种涉及一个新模板函数的潜在解决方案

#include <vector>

template <typename U, typename V>
void move_or_copy(std::vector<U>& y, std::vector<V>&& x) {
    if constexpr(std::is_same<U,V>::value) {
        y = std::move(x);
    } else {
        y.assign(x.begin(), y.end());
    }
}


using Scalar = double;

int main()
{
    std::vector<Scalar> x(10);
    std::vector<float> y(10);

    move_or_copy(y, x);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)


小智 9

当is not时,赋值y = std::move(x);无效,因为is 的类型和is 的类型,并且它们不直接兼容。Scalarfloatystd::vector<float>xstd::vector<Scalar>

#include <vector>
#include <type_traits>

using Scalar = double;

template <typename T, typename A>
void moveOrAssign(std::vector<T>& dest, std::vector<A>&& source) {
    if constexpr(std::is_same_v<T, A>) {
        dest = std::move(source);
    } else {
        dest.assign(source.begin(), source.end());
    }
}

int main() {
    std::vector<Scalar> x(10);
    std::vector<float> y(10);

    moveOrAssign(y, std::move(x));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 由于“source”是右值引用,因此可以通过使用 [`std::make_move_iterator`](https://en.cppreference.com/w/cpp/iterator/make_move_iterator) 来改进第二种情况,以避免复制中的元素可能很重要的情况。例如,当前的“moveOrAssign”实现可能会在“std::vector&lt;std::string&gt;”的情况下生成冗余副本,而在“std::vector&lt;std::”的情况下根本不起作用。 unique_ptr&lt;...&gt;&gt;`。 (2认同)