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

How can I move or assign one vector into another depending of their type using if constexpr?

提问人:T.L 提问时间:8/7/2023 最后编辑:Peter MortensenT.L 更新时间:8/10/2023 访问量:894

问:

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

如果是然后移动,否则复制。Scalarfloat

我试过这个代码

#include <vector>

using Scalar = double;

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

    if constexpr(std::is_same<Scalar, float>::value)
        y = std::move(x); // line 11 (does not compile)
    else
        y.assign(x.begin(), x.end());

    return 0;
}

但我明白了

main.cpp:11:24: error: no match for ‘operator=’ (operand types are ‘std::vector<float>’ and ‘std::remove_reference<std::vector<double>&>::type’ {aka ‘std::vector<double>’})
   11 |         y = std::move(x);
      |                        ^

我认为既然是在编译时计算的,那么下面的行就不会被编译。std::is_same<Scalar, float>::valuefalse

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

如何根据类型移动/分配到?xyScalar

C++ 模板 C++17 元编程

评论

18赞 HolyBlackCat 8/7/2023
if constexpr只有当条件取决于模板参数时,模板内部才会有魔力。
1赞 Drew Dormann 8/7/2023
HolyBlackCat 所指的“魔力”是丢弃未获取分支中的代码。您的语句仍然是 constexpr,但两个分支都已实例化。cp偏好if
0赞 Richard Ebeling 8/28/2023
这回答了你的问题吗?if constexpr - 为什么完全检查丢弃语句?

答:

10赞 T.L 8/7/2023 #1

正如 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;
}
9赞 Turan-Yahya Gazizuly 8/7/2023 #2

赋值在 不是 时无效,因为 是 类型 和 是 类型 ,并且它们不直接兼容。y = std::move(x);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;
}

评论

0赞 Jan Schultke 8/7/2023
我认为这个解决方案比另一个更优雅,因为它不需要过载分辨率,而且整体上更简短、更直观。
1赞 pptaszni 8/7/2023
但是这个代码是错误的,或者我不知道如何复制粘贴?godbolt.org/z/9xrhvP176两个向量都使用相同的模板参数,并且与 冲突。Tfloatdouble
0赞 Turan-Yahya Gazizuly 8/7/2023
您可以添加新的并使用它!typename
1赞 chrysante 8/7/2023
如果您编辑了错误,这将是一个很好的解决方案
2赞 François Andrieux 8/7/2023
由于是右值引用,因此第二种情况可以通过使用 std::make_move_iterator 来改进,以避免在可能重要的情况下复制元素。例如,当前实现可能会在 a 的情况下制作冗余副本,而在 .sourcemoveOrAssignstd::vector<std::string>std::vector<std::unique_ptr<...>>
15赞 Toby Speight 8/7/2023 #3

因为不是模板,所以两端都必须有效。要使用这种方式,它需要位于模板中。main()if constexprif 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));
}

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

评论

2赞 pptaszni 8/7/2023
是的,将集合作为模板参数,第二个参数将是真正的转发参考。目前,例如 是不可能的。template <typename Container1, typename Container2>move_or_copy(y, y);