c++ - std::list<std::reference_wrapper<T>> 的基于概念限制范围的 for 循环

标签 c++ for-loop c++20 concept reference-wrapper

我有课Foo和一个 std::list<std::reference_wrapper<Foo>>并希望使用基于范围的 for 循环迭代其元素:

#include <list>
#include <functional>
#include <iostream>


class Foo {
public:
    Foo(int a) : a(a) {}
    int a;
};

int main() {
    std::list<Foo> ls = {{1},{2},{3},{4}};
    std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
    
    for(auto &foo : refs) {
        std::cout << foo.get().a << std::endl;
    }

    for(Foo &foo : refs) {
        std::cout << foo.a << std::endl;
    }

    return 0;
}
注意附加 get() catch 时 auto ,正如我们推断类型 std::reference_wrapper<Foo> ,而在第二种情况下 foo已经隐式转换为类型 Foo&因为我们明确地捕获了这种类型。
我实际上是在寻找一种方法来捕捉 auto 但隐含地抛弃了 std::reference_wrapper隐含地为了不必打扰 get()方法一直在for body ,所以我尝试引入一个合适的概念并捕获这个,即我试过
//this is not legal code

template<typename T>
concept LikeFoo = requires (T t) {
    { t.a };
};

int main() {
    std::list<Foo> ls = {{1},{2},{3},{4}};
    std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));

    for(LikeFoo auto &foo : refs) {
        std::cout << foo.a << std::endl;
    }
    return 0;
}
并希望它能奏效。 clang然而推导出foo的类型至 std::reference_wrapper<Foo> ,因此实际上下面的代码将是正确的:
//this compiles with clang, but not with gcc

template<typename T>
concept LikeFoo = requires (T t) {
    { t.a };
};

int main() {
    std::list<Foo> ls = {{1},{2},{3},{4}};
    std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));

    for(LikeFoo auto &foo : refs) {
        std::cout << foo.get().a << std::endl;
    }
    return 0;
}
然而,gcc完全拒绝接受基于范围的 for 循环并提示 deduced initializer does not satisfy placeholder constraints ,因为它试图检查 LikeFoo<std::reference_wrapper<Foo>> ,当然评估为假,所以 gcc一个人都抓不到foo概念限制。出现两个问题:
  • 哪个编译器是正确的? LikeFoo auto& foo : refs有效吗?
  • 有没有办法自动捕获(可能受概念限制)foo : refs这样就可以避免必须写get()for -循环体?

  • 您可以在 Compiler explorer 上找到此示例。 .

    最佳答案

    Which of the compilers is correct? Should LikeFoo auto& foo : refs be valid?


    refs是一个范围 reference_wrapper<Foo>& , 所以 foo推导出对 reference_wrapper<Foo> 的引用- 没有名为 a 的成员.约束变量声明不会改变推导的工作方式,它只是有效地表现得像一个额外的 static_assert .

    Is there a way to auto-catch (possibly concept-restricted) foo : refs such that one can avoid having to write get() in the for-loop body?


    只需写信 refs ?不。但是你可以写一个范围适配器来转换你的范围 reference_wrapper<T>范围为 T& .标准库中已经有这样的东西了,transform :
    for (auto &foo : refs | std::views::transform([](auto r) -> decltype(auto) { return r.get(); })) {
    
    这是一口,所以我们可以让它自己命名适配器:
    inline constexpr auto unwrap_ref = std::views::transform(
        []<typename T>(std::reference_wrapper<T> ref) -> T& { return ref; });
    
    然后你可以写:
    for (auto &foo : refs | unwrap_ref) { ... }
    for (auto &foo : unwrap_ref(refs)) { ... }
    
    无论哪种方式,foo这里推断为 Foo .
    再多做一点工作,您就可以编写一个范围适配器来解包 reference_wrapper<T>但保留任何其他引用类型。

    关于c++ - std::list<std::reference_wrapper<T>> 的基于概念限制范围的 for 循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68610246/

    相关文章:

    python - 如何测试求和树?

    c++ - 未计算上下文中的 Lambda(需要表达式)

    c++ - C++20 中两种模块文件(接口(interface)和实现)的意义何在?

    c++ - C++中的线程共享数据

    c++ - 编译时计算迭代的可能性

    java - 这个 java for 循环条件有什么问题?

    php - false 时继续执行代码

    c++ - 为什么将 "&& true"添加到约束会使函数模板成为更好的重载?

    c++ - 如何确定 QTableWidget 的正确大小?

    c++ - 捕获错误并继续返回调用函数