Я хочу создать вектор слияния наддува со ссылками на переменные. Цель состоит в том, чтобы передать в функцию различное количество параметров и добавить их в вектор слияния. Из-за ссылочных типов я добавляю каждый элемент по одному с 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) в другое функция по значению?
Я отправил отчет об ошибке в 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, содержащее ссылку на временный объект, который недопустим. Есть две модификации для решения этой проблемы:
Первое решение (то же самое для 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, которая является действительной. В конце, представление преобразуется в вектор слияния с усилением, и ссылки разрешаются.
Второе решение (то же самое для 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 (только если это другой вид).
Других решений пока нет …