天道酬勤,学无止境

explicit qualification in C++ declaration

The following namespace definition fails to compile when the first declaration is commented out. If the first declaration of foo is uncommented, then it compiles just fine.

namespace Y
{
    //void foo();
    void ::Y::foo(){}
}

The relevant part in the standard (§8.3¶1) says:

When the declarator-id is qualified, the declaration shall refer to a previously declared member

I understand that this rule prevents the introduction of names into other namespaces. I wonder if that rule could be relaxed to allow for qualified-ids referring to the current namespace.

评论

CWG #482 is relevant:

According to 8.3 [dcl.meaning] paragraph 1, […]
This restriction prohibits examples like the following:

void f();
void ::f();        // error: qualified declarator

namespace N {
  void f();
  void N::f() { }  // error: qualified declarator
}

There doesn't seem to be any good reason for disallowing such declarations, and a number of implementations accept them in spite of the Standard's prohibition. Should the Standard be changed to allow them?

Notes from the April, 2006 meeting:

In discussing issue 548, the CWG agreed that the prohibition of qualified declarators inside their namespace should be removed.

So your code is valid if the first declaration of foo is present (as of about 2012; GCC has an open bug report). If not, however, your quoted wording still applies and renders the qualified declaration ill-formed. I see no reason to permit that case; it intuitively implies that the name has been declared already, since qualified name lookup must determine what it refers to.

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 为什么 C++ 不支持跨范围重载?(Why C++ does not support overloading across scopes?)
    问题 我相信这里已经给出了最好的答案:为什么派生类中的重写函数隐藏了基类的其他重载? 但我有点困惑,特别是声明: 为了覆盖此行为,需要用户进行显式操作:最初是对继承方法的重新声明(目前已弃用),现在显式使用 using 声明。 假设我有以下程序: #include <iostream> using namespace std; class Base { public: int f(int i) { cout << "f(int): "; return i+3; } }; class Derived : public Base { public: double f(double d) { cout << "f(double): "; return d+3.3; } }; int main() { Derived* dp = new Derived; cout << dp->f(3) << '\n'; cout << dp->f(3.3) << '\n'; delete dp; return 0; } 我有两个问题: 我可以假设,wrt 派生类对象, int f(int i)函数根本不存在。 由于名称隐藏,这不是继承的。 如果我必须在派生类中使用这个函数,我必须在派生类中再次定义它吗? 回答1 我可以假设,wrt 派生类对象,int f(int i) 函数根本不存在。 由于名称隐藏
  • C++11 gcc: explicit qualification in declaration? Standard ref?
    When the following C++11 program is compiled with gcc 4.7: extern int i; int ::i; int main() { } gcc complains that: error: explicit qualification in declaration of `i` Is this non-conformant behaviour? Where in the standard is this program deemed ill-formed? 8.3p1 seems to indicate that it should be allowed: If the qualifier is the global :: scope resolution operator, the declarator-id refers to a name declared in the global namespace scope. Update: From N3485 8.3p1: A list of declarators appears after an optional (Clause 7) decl-specifier-seq (7.1). Each declarator contains exactly one
  • C ++ 11 gcc:声明中的显式限定? 标准参考?(C++11 gcc: explicit qualification in declaration? Standard ref?)
    问题 当以下 C++11 程序使用 gcc 4.7 编译时: extern int i; int ::i; int main() { } gcc 抱怨说: error: explicit qualification in declaration of `i` 这是不符合规范的行为吗? 这个程序在标准中的哪个地方被认为是格式错误的? 8.3p1 似乎表示应该允许: 如果限定符是全局 :: 范围解析运算符,则声明符-id 指的是在全局命名空间范围中声明的名称。 更新: 从 N3485 8.3p1: 声明符列表出现在可选(第 7 条) decl-specifier-seq (7.1) 之后。 每个声明符都包含一个声明符 ID; 它命名声明的标识符。 出现在 declarator-id 中的 unqualified-id 应该是一个简单的标识符,除了一些特殊函数的声明(12.3、12.4、13.5)和模板特化或部分特化的声明(14.7)。 当 declarator-id 被限定时,该声明应引用该限定符所引用的类或命名空间的先前声明的成员(或者,在命名空间的情况下,是该命名空间的内联命名空间集合的元素(7.3 .1)) 或其专业化; 该成员不应仅仅通过声明符 id 的嵌套名称说明符指定的类或命名空间范围内的 using 声明引入。 限定声明符 id 的嵌套名称说明符不应以 decltype
  • 在变量类型后命名变量是一种不好的做法吗?(Is naming variables after their type a bad practice?)
    问题 我正在使用 STL 和 boost 也使用的下划线命名风格(而不是驼峰命名风格)对 C++ 进行编程。 但是,由于类型和变量/函数都以小写命名,因此如下的成员变量声明将导致编译器错误(或至少是麻烦): position position; 名为position的成员变量,其类型为position 。 我不知道如何命名它:它通常是一个位置,但它也是对象的位置。 在骆驼的情况下,这对编译器没问题: Position position; 但是在 C++ 中它会导致问题。 我不想切换到驼峰式大小写,因此使用匈牙利表示法或添加尾随下划线,所以我想知道:无论如何,将成员命名为类型是一种好习惯吗? 在 C 中,为此使用神秘的单字母变量是很常见的: int i; 但我觉得这有点神秘: position p; 我可以使用变量命名的任何经验法则来避免这种情况吗? 如果您需要处理一些事情,我的代码中有更多示例: mouse_over(entity entity) // Returns true if the mouse is over the entity manager &manager; // A reference to the framework's manager audio audio; // The audio subsystem 编辑: 我很好奇 Bjarne Stroustrup
  • What does the “explicit qualification in declaration” error message mean?
    battleutils.cpp:1037: error: explicit qualification in declaration of 'int32 battleutils::AbilityBenediction(CBattleEntity*, CBattleEntity*)' What does this error mean exactly? The first line here is 1037 (in battleutils.cpp): int32 battleutils::AbilityBenediction(CBattleEntity* PCaster, CBattleEntity* PTarget) { .... return blah; } In the header file under: namespace battleutils { is this: int32 AbilityBenediction(CBattleEntity* PCaster, CBattleEntity* PTarget); The .cpp file correctly includes the header file.
  • C ++与C ++ / CLI:虚函数参数的常量限定(C++ vs. C++/CLI: Const qualification of virtual function parameters)
    问题 [以下所有内容均使用Visual Studio 2008 SP1进行了测试] 在C ++中,参数类型的const限定不影响函数的类型(8.3.5 / 3:“删除了任何修改参数类型的cv限定符”) 因此,例如,在以下类层次结构中, Derived::Foo覆盖Base::Foo : struct Base { virtual void Foo(const int i) { } }; struct Derived : Base { virtual void Foo(int i) { } }; 考虑C ++ / CLI中类似的层次结构: ref class Base abstract { public: virtual void Foo(const int) = 0; }; ref class Derived : public Base { public: virtual void Foo(int i) override { } }; 如果然后我创建Derived的实例: int main(array<System::String ^> ^args) { Derived^ d = gcnew Derived; } 它编译没有错误或警告。 当我运行它时,它将引发以下异常,然后终止: ClrVirtualTest.exe中发生了类型为'System.TypeLoadException
  • Effective C++: Item 43 -- Know how to access names in templatized base classes.
    Problem: Unable to access names in templatized base class In example: class CompanyA { public: ... void sendCleartext(const std::string& msg); void sendEncrypted(const std::string& msg); ... }; class CompanyB { public: ... void sendCleartext(const std::string& msg); void sendEncrypted(const std::string& msg); ... }; ... // classes for other companies class MsgInfo { ... }; // class for holding information // used to create a message template<typename Company> class MsgSender { public: ... // ctors, dtor, etc. void sendClear(const MsgInfo& info) { std::string msg; create msg from info; Company
  • 为什么我不能在头文件中单独编写命名空间的层次结构?(Why I can't separately write namespace's hierarchy in the header file?)
    问题 我写了一些头文件。 我想单独声明命名空间层次结构(为了清楚起见),然后声明函数和类。 对我来说,它看起来像文档中的目录。 这对我来说非常方便:在一个地方查看命名空间的完整层次结构。 我这样写: // Namespaces hierarchy: namespace Bushman{ namespace CAD_Calligraphy{} //... } // Declarations of classes and functions class Bushman::CAD_Calligraphy::Shp_ostream{ public: explicit Shp_ostream(std::ostream& ost); }; 但是 MS Visual Studio 对头文件的这种创建方式大喊大叫。 我应该这样写: namespace Bushman{ namespace CAD_Calligraphy{ class Shp_istream{ public: explicit Shp_istream(std::istream& ist); }; } } 为什么第一个变体不起作用? 这是 C++ 或 IDE 的限制吗? PS我的附加问题是here。 谢谢你。 回答1 限制在 §9/1 中:“如果类头名称包含嵌套名称说明符,则类说明符应引用先前直接在嵌套名称所在的类或命名空间中声明的类
  • std::max - 需要一个标识符(std::max - expected an identifier)
    问题 我遇到了std::max问题。 我想不通。 int border = 35; int myInt = 2; int myOtherInt = 3; int z = std::max(myInt + 2 * border, myOtherInt + 2 * border); 我已经包含了算法标准标题。 当我将鼠标悬停在 max 上时,我得到: 错误:需要一个标识符 和一个编译错误: 错误 C2589: '(' : '::'右侧的非法标记错误 C2059:语法错误: '::' 怎么了? 回答1 危险的猜测,因为您使用的是 VC++ - 将其放在任何#include之前: #define NOMINMAX windows.h定义了名为min和max宏,如下所示: #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) Windows SDK 在 C++ 标准化之前就包含了这些宏,但是因为它们显然对 C++ 标准库造成了严重破坏,所以可以定义NOMINMAX宏来阻止它们被定义。 通常,如果您使用 C++(而不是 C)并包含windows.h ,请始终首先定义NOMINMAX 。 回答2 如果您使用 VC++,您可以在包含任何标题之前使用#define NOMINMAX
  • C++ 用于 std 和 boost 命名空间最佳实践 [重复](C++ using for std and boost namespace best practice [duplicate])
    问题 这个问题在这里已经有了答案: 10 年前关闭。 可能的重复: 在 C++ 中,您更喜欢显式命名空间还是“使用”? 我是一名 C# 开发人员,但我的朋友是一名 C++ 开发人员。 他向我展示了充满像std::for_each和boost::bind这样的调用的代码。 我在 C# 中使用过,并认为使用指令会影响代码的可读性和通常更快的开发。 例如,在 C# foreach 语句之前键入任何命名空间将是一件令人头疼的事。 我想知道对这种流行的命名空间使用的利弊是什么? 是否包含这些命名空间是最佳实践? 回答1 首先,让我们做两个区分: 1) 有 using 指令,如using namespace std; 并使用像using std::cout;这样的声明using std::cout; 2) 您可以将 using 指令和声明放在头文件 (.h) 或实现文件 (.cpp) 中 此外,使用指令和声明将名称带入写入它们的名称空间,即 namespace blah { using namespace std; // doesn't pollute the *global* namespace, only blah } 现在,就最佳实践而言,很明显,将 using 指令和声明放在全局命名空间的头文件中是一个可怕的禁忌。 人们会因此而讨厌您,因为任何包含该标头的文件都将污染其全局命名空间。
  • c ++默认赋值运算符(c++ default assignment operator)
    问题 int a[10]; int b[10]; a = b; // struct test { int a[10]; }; test a,b; a = b; 第一个代码不能编译,因为我们不能分配数组,但第二个可以。 类的默认赋值运算符不是简单地为每个数据成员调用赋值吗? 为什么第二个代码编译? 回答1 来自 C++11 草案,第 12.8 节: 非联合类 X 的隐式定义的复制/移动赋值运算符执行其子对象的成员复制/移动赋值。 X 的直接基类首先按照它们在基类说明符列表中的声明顺序赋值,然后按照它们在类定义中声明的顺序赋值 X 的直接非静态数据成员. 让 x 要么是函数的参数,要么是移动运算符的 xvalue 引用参数。 每个子对象都以适合其类型的方式分配: — 如果子对象是类类型,就好像通过调用 operator= 将子对象作为对象表达式,将 x 的相应子对象作为单个函数参数(就像通过显式限定;也就是说,忽略任何可能的虚拟覆盖更多派生类中的函数); — 如果子对象是数组,则以适合元素类型的方式分配每个元素; — 如果子对象是标量类型,则使用内置赋值运算符。 这里的重要部分是: if the subobject is an array, each element is assigned, in the manner appropriate to the element type;
  • 在C ++中使用完全限定的名称(Using fully qualified names in C++)
    问题 我是C ++的新手,我在需要它的项目中尝试并行学习该语言。 我正在使用一个相当流行且稳定的开源库来完成很多繁重的工作。 通过源读取,教程和代码样本库中,我注意到声明类型,其结果往往很长,冗长的,有很多的线时,他们总是使用完全合格的名称::的。 这被认为是C ++中的最佳实践吗? 有其他解决方法吗? 回答1 他们可能发现比回答尝试了示例代码但发现它不起作用的人提出的许多问题要容易得多,这仅仅是因为他们没有“使用”所涉及的命名空间。 做法各不相同-如果您正在处理具有大量不同库和名称冲突的大型项目,则可能希望始终如一地主动使用更多名称空间限定符,以便在添加新代码时不必再去做旧代码明确说明其要使用的内容。 从风格上讲,有些人更喜欢确切地知道所指的是潜在地必须挖掘或遵循IDE的“声明”功能(如果可用),而另一些人则喜欢简洁并仅在“例外”引用中看到更完整的名称空间限定到尚未包含的名称空间-更具上下文相关性。 避免使用“使用名称空间xxx;”也很正常。 在头文件中,因为包含该头的客户端代码将无法将其关闭,并且该名称空间的内容将永久转储到其默认“搜索空间”中。 因此,如果您在标头中查看代码,这就是它们可能更明确的原因之一。 与此相反,您可以在函数体之类的范围内(甚至在标头中)使用“使用名称空间”,并且不会影响其他代码。 在实现文件中使用一个您希望成为翻译单元中最终文件的命名空间
  • C ++枚举是否从0开始?(Do C++ enums Start at 0?)
    问题 如果我有一个没有为该enum分配数字的枚举,它的序数值为0吗? 例如: enum enumeration { ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE }; 我已经能够找到一个帖子,指出C99标准要求使用0序数。 但是我知道C ++忽略了C99标准中的几件事。 而且我还能够找到一个使用序数1见证编译器的帖子,我似乎还记得看到过这一点,尽管我不能说是多久了。 我真的很想看到一个针对C ++的答案,但我也想知道序数0是否成立,即使我在enum的中间指定了一个值也是如此: enum enumeration { ZERO, ONE, TWO, THREE = 13, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE }; 回答1 根据该标准[dcl.enum] 用仅枚举的枚举键声明的枚举类型是无作用域的枚举,并且其枚举数是无作用域的枚举数。 枚举键枚举类和枚举结构在语义上是等效的; 用其中之一声明的枚举类型是作用域枚举,其枚举器是作用域枚举器。 可选的标识符在范围枚举的声明中不应省略。 枚举库的type-specifier-seq应命名为整数类型; 任何简历资格都将被忽略。 声明无范围枚举的opaqueenum声明不应忽略枚举库。 枚举器列表中的标识符被声明为常量
  • C++进阶_Effective_C++第三版(七) 模板与泛型编程 Templates and Generic Programming
    模板与泛型编程 Templates and Generic Programming C++模板的最初设计目的是建立类型安全的容器如vector,list和map。后来发现模板有能力完成越来越多可能的变化。后面就出现了泛型编程,写出的代码和其所处理的对象类型彼此独立。STL算法如for_each,find和merge就是这类编程的成果。后面发现它可以被用来计算任何可计算的值。于是导出了模板元编程。创造出在C++编译器内执行并于编译完成时停止执行的程序。 41、了解隐式接口和编译期多态 Understand implicit interfaces and compile-time polymorphism. 面向对象编程世界总是以显式接口和运行期多态解决问题。举个例子,给定这样的class: class Widget{ public: Widget(); virtual ~Widget(); virtual std::size_t size() const; virtual void normalize(); void swap(Widget& other); … }; // 和这样的函数: void doProcessing(Widget& w) { if(w.size() >10 && w != someNastyWidget) { Widget temp(w); temp
  • 不同编译器调用的不同类型转换运算符(Different cast operator called by different compilers)
    问题 考虑以下简短的 C++ 程序: #include <iostream> class B { public: operator bool() const { return false; } }; class B2 : public B { public: operator int() { return 5; } }; int main() { B2 b; std::cout << std::boolalpha << (bool)b << std::endl; } 如果我在不同的编译器上编译它,我会得到不同的结果。 在 Clang 3.4 和 GCC 4.4.7 中,它打印true ,而 Visual Studio 2013 打印false ,这意味着它们在(bool)b处调用不同的转换运算符。 根据标准,哪种行为是正确的? 在我的理解中operator bool()不需要转换,而operator int()需要一个int到bool转换,所以编译器应该选择第一个。 const是否对此有所作为,编译器是否认为 const 转换更“昂贵”? 如果我删除const ,所有编译器同样会产生false作为输出。 另一方面,如果我将这两个类组合在一起(两个运算符都在同一个类中),那么所有三个编译器都会产生true输出。 回答1 标准规定: 派生类中的转换函数不会隐藏基类中的转换函数
  • 异常范围解析运算符(Unusual scope resolution operator)
    问题 今天在重构一些 C++ 代码时,我得到了一些代码,归结为以下内容 class x { public: void x::y(); }; x::范围解析运算符是否在这里做任何事情,它是一个错误,还是其他什么。 我最好的猜测是它是一些自动完成留下的人工制品,但我很想知道我是否遗漏了任何东西。 使用的编译器是VS2010 SP1。 回答1 这是一个错误,大多数编译器都会拒绝它。 例如,GCC 说 prog.cpp:4:10: error: extra qualification ‘x::’ on member ‘y’ [-fpermissive] void x::y(); ^ C++11 8.3/1 不允许使用冗余限定符: 甲声明符-ID不应除了一个成员函数或它的类的静态数据成员外,函数或其命名空间的一个命名空间外的可变构件,或显式的定义的定义或显式实例的定义是合格其命名空间之外的特化,或作为另一个类或命名空间成员的友元函数的声明。 这些例外都不适用于其类中的成员声明。
  • 如何修复“在C99模式之外使用的for循环初始声明” GCC错误?(How do I fix “for loop initial declaration used outside C99 mode” GCC error?)
    问题 我正在尝试解决3n + 1问题,并且有一个for循环看起来像这样: for(int i = low; i <= high; ++i) { res = runalg(i); if (res > highestres) { highestres = res; } } 不幸的是,当我尝试使用GCC进行编译时,出现了此错误: 3np1.c:15:错误:在C99模式之外使用了'for'循环初始声明 我不知道什么是C99模式。 有任何想法吗? 回答1 我会尝试在循环外声明i ! 祝您解决3n + 1时好运:-) 这是一个例子: #include <stdio.h> int main() { int i; /* for loop execution */ for (i = 10; i < 20; i++) { printf("i: %d\n", i); } return 0; } 在此处阅读有关C语言中的for循环的更多信息。 回答2 有一个启用C99模式的编译器开关,除其他功能外,该开关还允许在for循环内声明变量。 要打开它,请使用编译器开关-std=c99 或如@OysterD所说,在循环外声明变量。 回答3 要在CodeBlocks中切换到C99模式,请执行以下步骤: 单击“项目/构建选项” ,然后在“编译器设置”选项卡中选择“其他选项”子选项卡,然后在文本区域中放置-std
  • 带和不带指针声明符的 C++11 自动声明(C++11 auto declaration with and without pointer declarator)
    问题 bar1和bar2的类型有bar1 bar2 ? int foo = 10; auto bar1 = &foo; auto *bar2 = &foo; 如果bar1和bar2都是int* ,在bar2声明中写入指针声明bar2 ( * ) 是否bar2 ? 回答1 声明完全等效。 auto工作(几乎)与模板类型推导相同。 显式放置星号会使代码更易于阅读,并使程序员意识到bar2是一个指针。 回答2 使用auto * “文件意图”。 和auto *p = expr; 仅当expr返回指针时才能正确推导。 例子: int f(); auto q = f(); // OK auto *p = f(); // error: unable to deduce 'auto*' from 'f()' 回答3 使用const限定符时有很大的不同: int i; // Const pointer to non-const int const auto ip1 = &i; // int *const ++ip1; // error *ip1 = 1; // OK // Non-const pointer to const int const auto* ip2 = &i; // int const* ++ip2; // OK *ip2 = 1; // error 回答4 在此特定示例中,
  • C++:纯虚赋值运算符(C++: pure virtual assignment operator)
    问题 为什么如果我们在基类中有纯虚赋值运算符,然后我们在派生类上实现该运算符,它会在基类上产生链接器错误? 目前我在 http://support.microsoft.com/kb/130486 上只有以下解释,它说该行为是设计使然,因为正常的继承规则不适用。 我不清楚,为什么它会按设计生成链接器错误? 有人可以给我更清楚的解释吗? 编辑:添加了我发生错误的简化代码: class __declspec(dllexport) BaseClass { public: int memberA; virtual BaseClass& operator=(const BaseClass& rhs) = 0; }; class __declspec(dllexport) DerivedClass : public BaseClass { public: int memberB; DerivedClass():memberB(0) {} virtual BaseClass& operator=(const BaseClass& rhs) { this->memberA = rhs.memberA; this->memberB = 1; return *this; } }; int main(void) { DerivedClass d1; DerivedClass d2; BaseClass*
  • ms-extensions 标志对 gcc 有什么作用?(What does the ms-extensions flag do exactly with gcc?)
    问题 GCC 有一个标志-fms-extensions 。 这个标志究竟有什么作用? 为什么它有时默认开启,为什么它存在? 回答1 根据 gcc 9.1.0 源代码(grepped for flag_ms_extensions ),效果是: (C) 允许 Microsoft 版本的匿名联合和结构。 这包括对 C11 匿名联合和结构以及 Microsoft 特定风格的支持,包括完全省略大括号成员列表,并将成员放置在父命名空间中,即使结构/联合具有标识符。 (C++) 允许类成员具有与其类型相同的名称(例如using foo = int; struct A { foo foo; } )。 在禁用 ms-extensions 的情况下,行为是在 C 中接受此代码(在合法的地方); 或extern "C"块,除非给出了-pedantic标志。 此错误消息是declaration of NAME changes meaning of NAME 。 (C++) 允许隐式 int ; 现在允许任何会产生诊断ISO C++ forbids declaration of NAME with no type情况,并假定ISO C++ forbids declaration of NAME with no type为int 。 例子: const *p; 或const f(); . (C++)