将返回类型的函数参数的常量性与概念匹配
C++ 容器不包含 const 元素,例如您有const std::vector<int>,而不是std::vector<const int>。当我尝试根据传递的容器是否为常量来调整函数的返回值类型时,这有点不幸。
这是一个激励性的例子,请不要过多关注算法或boost的使用,我只使用它,因为C++ optional 不支持引用。
这段代码似乎可以工作,但代码看起来很丑,所以我想知道概念是否给我们提供了一种以更好的方式编写它的方法。我认为不是,因为基本上概念只是谓词,但我希望有一些好的东西,特别是返回类型非常垃圾。
template<typename C>
using match_const = std::conditional_t< std::is_const_v<std::remove_reference_t<C>>,
const typename std::remove_reference_t<C>::value_type,
typename std::remove_reference_t<C>::value_type>;
// no constraints
auto ofind(auto& container, const auto& value) -> boost::optional<match_const<decltype(container)>&> {
if (auto it = std::ranges::find(container, value); it!=container.end()){
return *it;
}
return boost::none;
}
// dummy concept
template<typename C>
concept Container = requires (C c){
{c.begin()};
{c.end()};
{c.size()} -> std::same_as<size_t>;
};
// constraints version
auto ofind2(Container auto& container, const auto& value) ->boost::optional<match_const<decltype(container)>&>{
if (auto it = std::ranges::find(container, value); it!=container.end()){
return *it;
}
return boost::none;
}
如果我的问题太模糊,这里是我的理想化版本:
boost::optional<Container::real_reference> ofind(Container auto& container, const auto& value)
其中 Container::real_reference 是与向量中的引用 typedef 不同的常量匹配的东西,例如考虑这个:
using CVI = const std::vector<int>;
static_assert(std::is_same_v<int&, CVI::reference>); // compiles
注意:我知道我应该让第二个参数也更加严格,但为了简单起见,我将它保留为const auto&.
回答
Ranges 已经为我们提供了一种获取任何范围的正确引用类型的方法:
std::ranges::range_reference_t<R>
如果R是vector<int>,那就是int&。如果R是vector<int> const,那就是int const&。如果R是span<int> const,那是...仍然int&是因为span是浅常量(这是你的特征出错的地方,因为它假设一切都是深常量)。
这个特性并不神奇,它所做的只是为您提供底层迭代器取消引用的类型:decltype(*ranges::begin(r))。
有了这个,你find可以看起来像:
template <range R>
auto ofind(R& container, const auto& value) -> boost::optional<range_reference_t<R>>;
请注意,如果您确实需要使用参数的类型,则使用缩写的函数模板语法实际上并不会因为必须编写而被全部缩写decltype,因此您可以……使用普通的函数模板语法。