I'm implementing a map function, with two overloads: one for rvalues and another one for const reference:
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;
}
And I have the following test:
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.});
}
Running it with Clang the test pass:
passed: almost_equal(map(std::negate<>(), v), {-1., -2., -3.}) for: true
passed: v == std::vector{1., 2., 3.} for: { 1.0, 2.0, 3.0 } == { 1.0, 2.0, 3.0 }
Passed 1 test case with 2 assertions.
But it fails with GCC:
passed: almost_equal(map(std::negate<>(), v), {-1., -2., -3.}) for: true
failed: v == std::vector{1., 2., 3.} for: { -1.0, -2.0, -3.0 } == { 1.0, 2.0, 3.0 }
Failed 1 test case, failed 1 assertion.
So GCC is calling the rvalue reference overload and is modifying the const defined vector.
GCC version is:
gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
Clang version is:
clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
EDIT:
So I tried more tests, this fails with 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.});
}
But if I add the template type parameters:
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.});
}
then it works !