我正在实现一个map函数,有两个重载:一个用于右值,另一个用于const引用:
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运行它测试通过:
通过:almost_equal(map(std::否定
传递:v==std::向量{1.,2.,3.}用于:{1.0,2.0,3.0 } == { 1.0,2.0,3.0}
通过1个测试用例,有2个断言。
但它失败了GCC:
通过:almost_equal(map(std::否定
失败:v==std::向量{1.,2.,3.}用于:{-1.0,-2.0,-3.0 } == { 1.0,2.0,3.0}
失败1个测试用例,失败1个断言。
因此GCC调用右值引用重载并修改const定义的向量。
GCC版本是:
gcc(Ubuntu 7.3.0-27ubuntu1~18.04)7.3.0
Clang版本是:
clang版本6.0.0-1ubuntu2(标签/RELEASE_600/最终)
编辑:
所以我尝试了更多的测试,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.});
}
然后它工作!
演示此问题的最小示例:
#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
联机编译器
错误80990-使用类模板参数推导在变量定义中忽略cv限定符