Добавить ссылки на boost :: fusion :: vector

Я хочу создать вектор слияния наддува со ссылками на переменные. Цель состоит в том, чтобы передать в функцию различное количество параметров и добавить их в вектор слияния. Из-за ссылочных типов я добавляю каждый элемент по одному с 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;
}

Программа аварийно завершает работу при обращении к ссылке в векторе ускоренного слияния (нарушение прав чтения), сначала в этой строке:

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 fusion (boost :: fusion :: join возвращает только представление, а представление преобразуется с помощью boost :: fusion :: vector в «нормальный» boost :: fusion :: vector) в другое функция по значению?

2

Решение

Я отправил отчет об ошибке в Microsoft Connect (ссылка на сайт). Так что ответ на мой вопрос заключается в том, что в моем коде есть ошибка.

Проблема состоит в том, что функция join возвращает форсированное объединение joint_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. Вектор ускоренного слияния не является представлением, а 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, и, как и в первой версии, представление будет преобразовано в вектор слияния наддува, и ссылки снова будут разрешены.

Спасибо Эрику Брумеру (Microsoft), который предоставил эти два решения.

Заключение:
Не передавайте временный объект в функцию Boost Fusion Join (только если это другой вид).

1

Другие решения

Других решений пока нет …