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

时间:2021-02-07 10:25:06

标签: c++ c++17 variadic-templates undefined-behavior

我正在尝试创建一个函数 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 << " ";
    }
}

附言是的,我应该使用 SFINEA 或概念。

2 个答案:

答案 0 :(得分:1)

还不确定您的问题出在哪里,您自己发现了:) ...但这就是我使用 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

答案 1 :(得分:1)

自己找到的...

{%- layout 'yourOtherLayout' -%}

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

当然应该

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