【问题标题】:Matching constness of function argument for return type with concepts将返回类型的函数参数的常量与概念匹配
【发布时间】:2021-06-28 18:34:10
【问题描述】:

C++ 容器不包含 const 元素,例如你有const std::vector<int>,而不是std::vector<const int>。 当我尝试根据传递的容器是否为 const 来调整函数的返回值类型时,这有点不幸。

这里是励志例子,请不要过多关注算法或boost的使用,我只使用它,因为C++ optional不支持引用。

这段代码appears to work,但是代码看起来很丑,所以我想知道概念是否为我们提供了一种以更好的方式编写它的方法。 我认为不是,因为基本上概念只是谓词,但我希望有一些好的东西,特别是返回类型非常垃圾。

    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&amp;

【问题讨论】:

  • 你不需要boost::optional&lt;decltype(*container.begin())&amp;&gt;吗?
  • 您不必为使用boost::optional而道歉,std::optional 只是随意缺少功能不是您的错。
  • "C++ 容器不包含 const 元素" - 如果您在示例中声明它们,它们会:std::vector&lt;const int&gt;
  • @Barry 我只是想抢占人们的注意力,因为我的问题基本上与 boost::optional 无关,它可能是 (gasp) 原始的是否为 const 的指针。 :)
  • @TedLyngmo 除了vector&lt;T const&gt; 不是一个东西。

标签: c++ c++20 c++-concepts


【解决方案1】:

Ranges 已经为我们提供了一种获取任何范围的正确引用类型的方法:

std::ranges::range_reference_t<R>

如果Rvector&lt;int&gt;,那就是int&amp;。如果Rvector&lt;int&gt; const,那就是int const&amp;。如果 Rspan&lt;int&gt; const,那仍然是 int&amp;,因为 span 是浅常量(这是你的 trait 出错的地方,因为它假定一切都是深常量)。

这个特性并不神奇,它所做的只是为您提供底层迭代器取消引用的精确类型:decltype(*ranges::begin(r))

这样,您的find 可以如下所示:

template <range R>
auto ofind(R& container, const auto& value) -> boost::optional<range_reference_t<R>>;

请注意,如果您确实需要使用参数的类型,由于必须编写decltype,因此使用缩写函数模板语法实际上并不会完全缩写,因此您可以只是...使用正常的函数模板语法。

【讨论】:

  • 为了 100% 清楚,我无法将 typedef/using 放在一个概念中,比如 Container::real_reference?
  • @NoSenseEtAl 号
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-22
  • 1970-01-01
相关资源
最近更新 更多