【问题标题】:C++ Concepts Same and AssignableC++ 概念相同且可分配
【发布时间】:2017-03-18 03:53:50
【问题描述】:

我最近一直在尝试 C++ 概念。我正在尝试以下 Ranges Extensions 文档中的定义:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf

Same 的定义和用法让我很困惑。由于我不知道的原因,作者没有给出明确的定义。所以我正在使用:

template <class T, class U>
concept bool Same()
{
  return std::is_same<T, U>::value;
}

问题是文档给出了Assignable的以下定义:

template <class T, class U>
concept bool Assignable()
{
  return Common<T, U>() && requires(T&& a, U&& b) {
    { std::forward<T>(a) = std::forward<U>(b) } -> Same<T&>;
  };
}

它不起作用(在 GCC 6.3 下):一个简单的Assignable&lt;int&amp;, int&amp;&amp;&gt;() 概念检查给了我false(我已经验证Common 部分没问题)。我必须将 Same&lt;T&amp;&gt; 更改为 T&amp; 以使其看起来有效。同样的Same&lt;Type&gt;检查也用于其他一些地方。

我的问题是:

  • 我对@9​​87654332@ 的定义是否正确?
  • 为什么使用Same&lt;T&amp;&gt; 而不是T&amp;?有什么区别?

感谢您的帮助。

【问题讨论】:

    标签: c++ c++-concepts


    【解决方案1】:

    在周末攻克这个问题后,我想我自己找到了答案。

    Eric Niebler 和 Casey Carter 对 Same 有一个更精确的定义,它支持多个模板参数(不仅仅是两个),但我的定义应该基本上适用于两个参数的情况。

    使用-&gt; Type时,目的是可以将括号内的表达式隐式转换为Type。使用-&gt; Same&lt;Type&gt;时,目的是括号中的表达式正好是Type。所以它们是不同的。

    但是,有一个问题。约束检查相当复杂,甚至像 Eric 和 Casey 这样的专家也在 N4569 中犯了错误并给出了错误的定义。 Eric 在 GitHub 上讨论了这个问题:

    https://github.com/ericniebler/stl2/issues/330

    当使用它在 N4569 中给出的方式时,这意味着表达式应该能够传递给一个想象的函数模板,如

    template <typename U>
    f(U)
    requires Same<T&, U>()
    

    这不起作用——如果传入的表达式是T 的左值,则推导出的UT 而不是T&amp;。解决方案是在Assignable 中使用Same&lt;T&amp;&gt;&amp;&amp;。它将产生以下想象的函数模板:

    template <typename U>
    f(U&&)
    requires Same<T&, U>()
    

    现在一切正常——如果传入的表达式是左值TU 必须推导出为T&amp;

    玩概念对我来说是个好习惯,但我可能应该早点找到他们的代码。他们在以下 GitHub 存储库中有一套完整的概念:

    https://github.com/CaseyCarter/cmcstl2

    对 C++ 概念感兴趣的人应该研究一下。

    【讨论】:

    • Same 定义的问题有点微妙:Ranges TS 要求实现将约束 Same&lt;T, U&gt;() 视为等同于 Same&lt;U, T&gt;()。任何简单的定义都不可能做到这一点。它不能用语言表达,实际上需要通过编译器内在函数实现Same
    • 您对Assignable 中举例说明的问题的评估是完全正确的:当我实现 cmcstl2 时,GCC 中的推导约束被打破,并且我在开发一种解决方法语法时巧妙地误解了语义-相等的。因此,直到最近我们才意识到 TS 中的概念定义被破坏了。
    • @Casey 我实际上认为您的错误更多是当前概念提案的失败。使用 Same&lt;T&amp;&gt;&amp;&amp; 看起来很丑,而且一点也不直观。
    • @Casey 您能否详细说明我的定义(基于std::is_same)何时会失败而基于内在的定义不会?听起来很有趣。
    • 在附加答案中详细说明。
    【解决方案2】:

    Same 的定义问题有点微妙:Ranges TS 要求实现将约束Same&lt;T, U&gt;() 视为等同于Same&lt;U, T&gt;(),即识别“T 的对称性是相同的输入U":

    template <class T, class U>
    requires Same<T, U>()
    void foo(); // #1
    
    template <class T, class U>
    requires Same<U, T>()
    void foo(); // redeclaration of #1
    
    template <class T>
    requires Same<int, T>()
    void bar(); // #2
    
    template <class T>
    requires Same<T, int>()
    void bar(); // redeclaration of #2
    

    这种等价性无法用语言表达,因为约束规范化的规则可以识别以下各项:

    is_same_v<T, U>
    is_same_v<U, T>
    is_same_v<T, int>
    is_same_v<int, T>
    

    作为不同的原子约束。这需要通过编译器内部实现Same

    【讨论】:

    • 感谢您的回答。您是否有一个真实世界或人为的案例,其中差异会导致问题?抱歉,我就是想不通。
    • 我不知道现实世界中限制通用代码自然会导致这种情况的例子。安德鲁萨顿可能知道一些? IIRC 这个要求的最初建议来自他。
    猜你喜欢
    • 1970-01-01
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 2015-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-08
    相关资源
    最近更新 更多