在我的可变参数模板中获取 UB 按元素对可变数量的向量求和

Getting UB in my variadic template summing a variable number of vectors element-wise

我正在尝试创建一个函数 AddVector 以元素方式添加(可变)数量的向量。我想我明白了,但是当我得到错误的输出时,我显然没有。我添加了三个双精度向量,每个大小为 5,包含 1+2+1,因此我期望

4 4 4 4 4

我明白了

1.36234e-316 2.0326e-316 4 4 4 

哪个明显错误(可能是未初始化的内存?)

我用 CppInsights 查看了翻译后的代码,但看起来也不错。我在这里做错了什么?

我的代码:

#include <vector>
template<typename T>
using Vec = std::vector<T>;

template<typename T>
auto SumAndIncVcIt(T& t) {
    return *t++;
}

template<typename T, typename... Args>
auto SumAndIncVcIt(T& t, Args&... args) {
    return *t++ + SumAndIncVcIt(args...);
}

#include <tuple>
template<typename... Args>
auto VcBegins(Args... args){
    return std::make_tuple(cbegin(args)...);
}

template<typename T, size_t... Is>
auto SumAndIncVcIts_impl(T& t, std::index_sequence<Is...>) {
    return SumAndIncVcIt(std::get<Is>(t)...);
}

template<class Tuple>
auto SumAndIncVcIts(Tuple& t) {
    return SumAndIncVcIts_impl(t,
        std::make_index_sequence<std::tuple_size<Tuple>{}>{}
    );
}

template<typename T, typename... Args>
Vec<T> AddVector(Vec<T> const& vt, Vec<Args> const&...  vargs){
    auto vret = Vec<T>(size(vt));
    auto vcIts = VcBegins(vt, vargs...);
    auto retIt = begin(vret);
    while(retIt != end(vret)){
        *retIt++ = SumAndIncVcIts(vcIts);
    }
    return vret;
}

#include<iostream>

int main() {
    constexpr auto size = 5;
    Vec<double> a(size, 1.0), b(size, 2.0);
    auto c = AddVector(a, b, a);

    for(auto const& el : c){
        std::cout << el << " ";
    }
}

p.s。是的,我应该使用 SFINEA 或概念。

还不确定你的问题出在哪里,你自己发现的:) ...但这就是我用 C++17 和大小实现它的方式:

#include <vector>
#include <algorithm>

template<typename T>
using Vec = std::vector<T>;

template<typename T, typename...Args>
Vec<T> AddVector_impl(Vec<Args> const & ... vecs){
    auto sizes = {vecs.size()...};
    auto new_size = *std::min_element(sizes.begin(),sizes.end());

    Vec<T> res(new_size);

    for(std::size_t i=0;i<new_size;++i){
        res[i]=(vecs[i]+...);
    }
    return res;
}

// Ensures at least one vector, also sets its return type.
template<typename T, typename... Args>
Vec<T> AddVector(Vec<T> const& vt, Vec<Args> const&...  vargs){
    return AddVector_impl<T>(vt,vargs...);
}

#include <iostream>
int main() {
    constexpr auto size = 5;
    Vec<double> a(size, 1.0), b(size, 2.0);
    auto c = AddVector(a, b, a);

    for(auto const& el : c){
        std::cout << el << " ";
    }
}

Live Godbolt demo

输出:

4 4 4 4 4 

仅迭代器解决方案

#include <vector>
#include <algorithm>

template<typename T>
using Vec = std::vector<T>;


template<typename T, typename...Args>
Vec<T> AddVector_impl(Vec<Args> const & ... vecs){
    auto its = std::tuple(vecs.cbegin()...);

    auto add_inc = [](auto&... iters){
        return ((*iters++) + ... );
    };
    auto end_check = [&](auto&...iters){
        return ((iters!=vecs.cend()) && ...);
    };
    Vec<T> res;
    auto it = std::back_insert_iterator(res);
    while(std::apply(end_check,its)){
        *it++=std::apply(add_inc,its);
    }
    return res;
}

template<typename T, typename... Args>
Vec<T> AddVector(Vec<T> const& vt, Vec<Args> const&...  vargs){
    return AddVector_impl<T>(vt,vargs...);
}

#include <iostream>
int main() {
    constexpr auto size = 5;
    Vec<double> a(size, 1.0), b(size, 2.0);
    auto c = AddVector(a, b, a);

    for(auto const& el : c){
        std::cout << el << " ";
    }
}

Live Godbolt demo.

自己找的...

template<typename... Args>
auto VcBegins(Args... args){
    return std::make_tuple(cbegin(args)...);
}

按值传递,因此向量被复制,我得到无效的迭代器

当然应该

template<typename... Args>
auto VcBegins(Args const&... args){
    return std::make_tuple(cbegin(args)...);
}