【问题标题】:Concepts and declaration order概念和声明顺序
【发布时间】:2016-03-20 15:10:35
【问题描述】:

我一直在 SVN 的 GCC 中尝试精简概念。我遇到了一个我怀疑是由于我缺乏理解的问题,如果有人能指出我正确的方向,我将不胜感激。我的代码是:

#include <iostream>
#include <string>

// Uncomment this declaration to change behaviour
//void draw(const std::string&);

template <typename T>
concept bool Drawable() {
    return requires (const T& t) {
        { draw(t) }
    };
}

void draw(const std::string& s)
{
    std::cout << s << "\n";
}

int main()
{
    static_assert(Drawable<std::string>()); // Fails
}

在这里我定义了一个简单的概念,Drawable,它的目的是要求给定类型为const T&amp; 的参数,函数draw(t) 进行编译。

然后我定义了一个函数draw(const std::string&amp;),它将字符串“绘制”到cout。最后,我检查std::string 是否与Drawable 概念匹配——这是我预料到的,因为当调用static_assert 时,适当的draw() 函数在范围内。

但是,静态断言会失败,除非我在概念定义之前包含draw(const std::string&amp;) 的声明,我不知道为什么。

这是与概念有关的预期行为,还是我做错了什么?

【问题讨论】:

  • 具有数百个骗子的沼泽标准 ADL 问题。 ADL 不会检查全局命名空间,因为所涉及的类型都不是它的成员。取消注释模板上方的声明使 unqualified-name-lookup 能够在模板定义上下文中找到它。
  • 因此,如果他将那个 void draw 声明放在命名空间 std。它应该可以工作。
  • 我在开头的句子中缺少 SVN 参考。这有什么意义吗?
  • @Columbo 这实际上是有道理的,使用常规函数定义我可能最终会弄明白,但概念问题让我很困惑。把它写成答案,如果你愿意,我会接受它
  • @Columbo 这与 ADL 无关,它与在定义点或实例化点查找名称有关。 “工作”draw 和“损坏”draw 都在同一个命名空间中。

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


【解决方案1】:

因为,上面的函数需要声明,以便所有使用它的函数都需要知道它。

在一个类中,函数在头文件中声明。如果它们不是成员,则需要在使用之前声明它们。这是因为编译器从上到下读取,只有看到声明才知道函数。

如果您交换概念代码和绘图代码,它也应该可以工作。

【讨论】:

    【解决方案2】:

    该问题与 ADL 无关)而仅与名称查找有关。 GCC 使用的概念草案是 n4377,但我将使用的 C++ 标准草案是 n4140。首先,在深入研究标准语言之前,我们可以将您的问题转化为我们知道应该可以工作的 MCVE。示例:

    template<typename T> concept bool C =
      requires (T a, T b) {
        a + b;
      };
    

    这是一个简单的需求,[expr.prim.req.simple],用于检查表达式的有效性。重写我们的示例以匹配表单:

    template<typename T> concept bool Drawable = 
      requires (const T& x) { 
        draw(x); 
      };
    

    我们可以看到我们的语法很好。好的,n4377 说什么?

    [expr.prim.req]/1 requires-expression 提供了一种简洁的方式 表达对模板参数的要求。一个要求是 可以通过名称查找(3.4)或通过检查类型的属性来检查 和表达式。

    [expr.prim.req]/6 需求主体由一系列 要求。这些要求可参考当地参数, 模板参数,以及任何其他可见的声明 封闭上下文。 ...

    有道理。我们知道 enclosure context 是全局命名空间,那么 n4140 是怎么说的呢?

    [basic.lookup.unqual]/1 在 3.4.1 中列出的所有情况下,范围 按每个列表中列出的顺序搜索声明 各自的类别;名称查找在声明后立即结束 找到名字。如果没有找到声明,程序是 格式不正确。

    在函数的定义中使用的名称 declarator-id 是命名空间N 的成员(其中,仅出于说明的目的,N 可以表示全局范围)应为 在使用它之前在使用它的块中或在其中之一中声明 它的封闭块(6.3)或,应在其使用前声明 命名空间N ...

    由于概念与功能有关,因此适用上述段落。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-12
      • 1970-01-01
      • 1970-01-01
      • 2017-04-21
      • 2019-05-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多