2

我想创建一个引用变量的提升融合向量。目标是将不同数量的参数传递给函数并将它们添加到融合向量中。由于引用类型,我使用 TMP 一次添加每个元素。但有时融合向量中的某些元素是错误的。这似乎是未定义的行为(错误的值,读取访问冲突)。
我写了一个示例,在其中我“展开”了 TMP 中使用的递归以便于理解。它只是简单地添加两个对融合向量的引用并输出结果:

#include <iostream>

#include <boost/ref.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/container.hpp>

using namespace boost;
using namespace boost::fusion;

//add second element
template <typename T>
vector<int&, double&> createVector2(T vec, double& v2) {
     auto newVector = join(vec, make_vector(ref(v2)));
     return newVector;
}

//add first element
template <typename T>
vector<int&, double&> createVector(T vec, int& v1, double& v2) {
     auto newVector = join(vec, make_vector(ref(v1)));
     return createVector2(newVector, v2); 
}

int main() {
     int v1 = 10;
     double v2 = 15.3;

     vector<> vec;
     auto ret = createVector(vec, v1, v2);

     std::cout << at_c<0>(ret) << std::endl;
     std::cout << at_c<1>(ret) << std::endl;

     if (at_c<0>(ret) != v1) {
         std::cout << "FAILED" << std::endl;
     }

     if (at_c<1>(ret) != v2) {
         std::cout << "FAILED" << std::endl;
     }

     return 0;
}

当访问 boost fusion 向量中的引用(读取访问冲突)时,程序崩溃,首先在此行:

std::cout << at_c<0>(ret) << std::endl;

我使用 VC11 作为编译器(版本 17.00.51106.1)。该错误仅在发布模式下。但是当我使用 VC10、GCC 4.7.0 或 GCC 4.7.2 时,没有错误,程序运行良好。
要让程序与 VC11 一起工作,我必须更改这一行

auto newVector = join(vec, make_vector(ref(v1)));

auto newVector = as_vector(join(vec, make_vector(ref(v1))));

那么,上面的示例是否包含错误或 VC11 优化器有问题?是否允许将本地 boost 融合视图(boost::fusion::join 仅返回一个视图,并且该视图通过 boost::fusion::vector 转换为“正常” boost::fusion::vector)到另一个按价值运作?

4

1 回答 1

1

我在 Microsoft connect (链接) 提交了一份错误报告。所以我的问题的答案是我的代码中有一个错误。

问题是join函数返回一个boost fusionjoint_view,里面包含seq1和seq2这两个元素。它们被定义为:

template <typename Sequence1, typename Sequence2>
struct joint_view : sequence_base<joint_view<Sequence1, Sequence2> >
{
    (...)
private:
    typename mpl::if_<traits::is_view<Sequence1>, Sequence1, Sequence1&>::type seq1;
    typename mpl::if_<traits::is_view<Sequence2>, Sequence2, Sequence2&>::type seq2;
};

我的代码的问题是我将一个临时对象(从 make_vector 返回)传递给了 join 函数。boost 融合向量不是视图,seq1 和 seq2 是参考。所以join函数返回一个joint_view,其中包含一个临时对象的引用,这是无效的。有两个修改可以解决这个问题:

  1. 第一个解决方案(与 createVector 相同):

    template <typename T>
    vector<int&, double&> createVector2(T vec, double& v2) {
        auto x = make_vector(ref(v2));
        auto newVector = join(vec, x);
        return newVector;
    }
    

    现在 join 返回一个joint_view,其中包含对x 的引用,这是有效的。最后,将视图转换为增强融合向量并解析参考。

  2. 第二种解决方案(与 createVector 相同):

    template <typename T>
    vector<int&, double&> createVector2(T vec, double& v2) {
        return join(vec, make_vector(ref(v2)));
    }
    

    临时对象(由 make_vector 返回)的生命周期是整个 return 语句,并且在第一个版本中,视图将转换为 boost 融合向量并再次解析引用。

感谢提供这两个解决方案的 Eric Brumer (Microsoft)。

结论:不要将临时对象传递给 boost fusion join 函数(仅当它是另一个视图时)。

于 2013-04-18T07:01:11.603 回答