【问题标题】:How to use a C++ requires clause in a concept to require a member variable to satisfy a concept constraint?如何在概念中使用 C++ requires 子句来要求成员变量满足概念约束?
【发布时间】:2020-11-30 11:47:27
【问题描述】:

我正在观看 C++ 20 Concepts Presentation ,在尝试重现代码时,我似乎被卡住了。

我试图要求树的根应该满足 MyObjConcept0_,为简单起见,它只是一个 int。当我在 Tree_ 概念的 requires 子句中使用这个概念时,为什么结果是错误的?

我尝试直接从演示文稿中复制代码,但仍然没有成功。为什么 { t.root } 子句的返回类型是 int& - 我的意思是它是有道理的,因为当您以这种方式访问​​成员时,您会获得引用。

那么为什么在 39:00 的演示文稿中 this(与 MyObjConcept0_ 相同)需要通过子句?

从本次演示开始,标准是否发生了变化,还是我盲目地遗漏了什么?

#include <concepts>
#include <functional>

// Type is an int
template<typename T>
concept MyObjConcept0_ = std::same_as<T,int>;

// Type is any type that decays to int
template<typename T>
concept MyObjConcept1_ = std::same_as<std::decay_t<T>,int>;

// Type is an int&
template<typename T>
concept MyObjConcept2_ = std::same_as<T,int&>;



template<typename T>
concept Tree_ = requires (T t) {
    { t.root } -> MyObjConcept0_;          // does not work : This is the concept I want to use  
    { t.root } -> MyObjConcept1_;          // works but will pass for int and int& : unsafe
    { t.root } -> MyObjConcept2_;          // works but checks that t.root is an int&
    std::same_as<decltype(t.root),int>; // works: verbose and not a concept
};

template<MyObjConcept0_ MyObjConcept0T>
struct tree {
    MyObjConcept0T root;
};

static_assert(Tree_<tree<int>>);

【问题讨论】:

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


    【解决方案1】:

    复合要求

    { e } -> Concept;
    

    意味着e 必须是一个有效的表达式并且Concept&lt;decltype((e))&gt; 必须成立。注意双括号,这很重要。让我们来一个更简单的树,我不知道为什么需要一个模板:

    struct X {
        int root;
    };
    
    X t;
    

    虽然decltype(t.root)int(该成员变量的声明类型是int),但decltype((r.root))int&amp;(因为它是int 类型的左值,因此是int&amp;)。结果:

    template <typename T>
    concept Tree = requires(T t) {
        { t.root } -> std::same_as<int&>;
    };
    

    Tree&lt;X&gt; 成立 - 因为t.rootint 类型的左值。


    clang 只是弄错了。它没有实现P1084,这是#45088

    【讨论】:

    • 不幸的是,这个答案是不正确的,因为如果 t.root 类型是 int&,它也会满足这个概念。在 GCC 10.2 上编译。 struct tree_intref { int& root; }; // 不应该满足树的概念
    • @BigTeeny 答案是正确的——你现在只是在问一个不同的问题。如果要检查成员的特定类型,则不能使用compound-requirement,只需写requires same_as&lt;decltype(T::root), int&gt;
    • 谢谢,我现在明白我的错误了。我将更好地重新格式化问题,因为似乎重点似乎是 int 类型,而不是将概念作为要求传递给复合要求子句中的非静态类成员,如 39:00 的演示文稿中所示。跨度>
    • 另外补充一下,根据clang.llvm.org/cxx_status.html#cxx20,Concepts P1084R2 可从 clang 10 获得。
    • @BigTeeny 好吧,这个状态是不正确的,因为实现做错了。
    【解决方案2】:

    表达式的需求检查类型。为了评估一个变量的类型,你必须检查一个对你的变量的decltype进行编码的表达式。

    template <typename T>
    struct wrap{};
    
    // member declared as `int`
    { wrap<decltype(t.intv)>() } -> std::same_as<wrap<int>>;
    
    // member declared as `int&`
    { wrap<decltype(t.intlrefv)>() } -> std::same_as<wrap<int&>>;
    
    // member declared as `int&&`
    { wrap<decltype(t.intrrefv)>() } -> std::same_as<wrap<int&&>>;
    

    【讨论】:

    • 此解决方案适用于 GCC 10.2 和 Clang 10.0,但我必须明确指定类型 int。我想将另一个概念作为对成员变量 t.root 的约束而不是显式类型。
    • 类似这样的东西(伪代码):模板 概念 Tree_ = requires(T t) { { Wrapper() } -> std::same_as>; };
    • wrap 增加了很多输入,但是......为什么?写requires std::same_as&lt;decltype(t.intv), int&gt;;?
    【解决方案3】:

    谢谢大家或您的回答,他们为我指明了正确的方向,但它们对我的问题还不够通用。因为关键问题是将概念类型传递给另一个概念的 requires 子句。我应该--of-指定。

    在编译器资源管理器上几个小时后,我找到了一个解决方案。 在 clang 10.0.0 及更高版本中使用 -std=c++20 :

    #include <type_traits>
    #include <functional>
    
    
    template<typename T>
    concept Node_ = std::is_object_v<T>; 
    
    template<typename T>
    concept Tree_ = requires (T t) {
        { t.root } -> Node_;
        /* implement child tuple constraints here */
    };
    
    // Generic tree object 
    template<Node_ NodeT /*, Tree_ ... ChildrenT*/>
    struct tree {
        NodeT root;
        /* implement child tuple here */
    };
    
    struct test_tree_int { int root; }; // Should satisfy tree concept
    struct test_tree_intref { int& root; }; // Shouldnt satisfy tree concept
    
    
    static_assert(Tree_<tree<int>>);
    static_assert(Tree_<test_tree_int>);
    //static_assert(Tree_<test_tree_intref>); /// Compilation fails
    

    这对我之前不起作用的原因是因为我使用的是 GCC 10.2,它实际上确实存在 Barry 提到的编译器问题:Concept 中的双括号会产生参考。

    似乎所有解决方案都会增加代码膨胀,这将使使用这些概念比帮助更麻烦。

    【讨论】:

    • 正如我在回答中所说,这是不正确test_tree_int not 满足 Treetest_tree_intref 也不满足...事实上,没有类型可以)。不是 gcc 有“编译器问题” - gcc 是正确的,这是语言规则。这里是错误的。
    • 感谢您抽出宝贵时间,我显然误解了一些东西,如果可能,请确认一下。演示文稿中显示的代码(39:00 - 40:00,相同)无效?我正在尽力模仿那个确切的系统,但无济于事。遗憾的是,该演示文稿没有可用的源代码。
    • 终于找到了来源:github.com/saarraz/slides/blob/master/…,第 62 行。您可以看到 requires 语句的这种确切用法。在 clang 而不是 gcc 上编译。
    • 是的。同样,它在 clang 上编译,因为 clang 错误地实现了语言规则。 { t.root } -&gt; Node 表示 Node&lt;decltype((t.root))&gt;,而不是 Node&lt;decltype(t.root)&gt; - 因此,没有任何类型可以满足 Tree
    猜你喜欢
    • 2023-01-11
    • 1970-01-01
    • 2022-12-13
    • 2020-01-27
    • 2018-04-25
    • 1970-01-01
    • 2016-10-05
    • 2021-12-26
    • 1970-01-01
    相关资源
    最近更新 更多