2
votes

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 !

1
The first overload does not accept an rvalue reference. It accepts a universal reference. Right now I can't get the stated behavior. You should post compiler arguments as well. Aren't you using default C++ standard by any chance?user7860670
I'm compiling in -O3 for C++17 -std=c++17user3719401
Does it still happen with -O0?user7860670
I edited my question: when I add the template type parameter for vector then the test succeed with GCC.user3719401
Also it still fails when compiling with -O0user3719401

1 Answers

2
votes

Minimal example to demonstrate this problem:

#include <vector>
#include <type_traits>

const ::std::vector<double> v1{1., 2., 3.};
const ::std::vector v{1., 2., 3.};
static_assert(::std::is_same_v<decltype(v1), decltype(v)>); // error on gcc

online compiler

Bug 80990 - cv-qualifiers ignored in variable definition using class template argument deduction