Я реализую функцию карты с двумя перегрузками: одна для rvalues и другая для константной ссылки:
template <class Array, class UnaryOp>
void map_inplace(UnaryOp op, Array &a) {
std::transform(a.cbegin(), a.cend(), a.begin(), op);
}
template <class Array, class UnaryOp>
auto map(UnaryOp op, Array &&a) {
map_inplace(op, a);
return a;
}
template <class Array, class UnaryOp>
auto map(UnaryOp op, const Array &a) {
Array res(a);
map_inplace(op, res);
return res;
}
И у меня есть следующий тест:
TEST_CASE("map") {
const std::vector v{1., 2., 3.};
// I do expect the const reference overload to be called
REQUIRE(almost_equal(map(std::negate<>(), v), {-1., -2., -3.}));
// Check const vector is not modified
REQUIRE(v == std::vector{1., 2., 3.});
}
Запустив его с Clang, тестовый проход:
пройден: почти_эквивалент (map (std :: negate <> (), v), {-1., -2., -3.}) Для: true
передано: v == std :: vector {1., 2., 3.} для: {1.0, 2.0, 3.0} == {1.0, 2.0, 3.0}
Пройдено 1 контрольный пример с 2 утверждениями.
Но с GCC ничего не получается:
пройдено: почти_эквивалент (map (std :: negate <> (), v), {-1., -2., -3.}) Для: true
не удалось: v == std :: vector {1., 2., 3.} для: {-1.0, -2.0, -3.0} == {1.0, 2.0, 3.0}
Неудачный тестовый пример 1, неудачное утверждение 1.
Таким образом, GCC вызывает ссылочную перегрузку rvalue и модифицируетопределенный константой вектор.
Версия GCC:
gcc (Ubuntu 7.3.0-27ubuntu1 ~ 18.04) 7.3.0
Версия Clang:
лязг версиина 6.0.0-1ubuntu2 (tags / RELEASE_600 / final)
РЕДАКТИРОВАТЬ:
Итак, я пробовал больше тестов, это не с GCC:
TEST_CASE("map") {
const std::vector v{1., 2., 3.};
REQUIRE((map(std::negate<>(), v) == std::vector{-1., -2., -3.}));
REQUIRE(v == std::vector{1., 2., 3.});
}
Но если я добавлю параметры типа шаблона:
TEST_CASE("map") {
const std::vector<double> v{1., 2., 3.};
REQUIRE((map(std::negate<>(), v) == std::vector<double>{-1., -2., -3.}));
REQUIRE(v == std::vector<double>{1., 2., 3.});
}
, то это сработает!