英文:
`using` inside a namespace in C++ module is not limited to the namespace
问题
以下是代码部分的翻译:
module;
#include "example.h" // `namespace cxx { struct example {}; }`
export module example;
export namespace cxx::containers
{
using cxx::example;
}
cxx::containers::example
becomes accessible from cxx::example
as well where I only expected cxx::containers::example
to appear.
import example;
int main()
{
auto v1 = cxx::containers::example();
auto v2 = cxx::example();
}
What is the reason that cxx::example
is visible even though I am only using it inside the namespace cxx::containers::example
and that #include
directives in the global module;
fragment should be localized to their module?
英文:
Given the following module example
module;
#include "example.h" // `namespace cxx { struct example {}; }`
export module example;
export namespace cxx::containers
{
using cxx::example;
}
cxx::containers::example
becomes accessible from cxx::example
as well where I only expected cxx::containers::example
to appear.
import example;
int main()
{
auto v1 = cxx::containers::example();
auto v2 = cxx::example();
}
What is the reason that cxx::example
is visible even though I am only using it inside the namespace cxx::containers::example
and that #include
directives in the global module;
fragment should be localized to their module?
答案1
得分: 7
"the reason that cxx::example
is visible" is that it's an MSVC bug: the declaration of cxx::example
isn't exported, so it isn't found by name lookup outside the module in question (basic.lookup.general/2.3, except in certain circumstances involving dependent ADL (basic.lookup.argdep/4)).
Since C++20 was published, the wording for using-declarations has been significantly revised to clarify the relationship between the original and new declaration. A using-declaration is not a redeclaration of the original entity or typedef (which is why it can be separately exported); instead, name lookup replaces a using-declarator with the declarations to which it refers (basic.lookup.general/3). The using-declaration for cxx::containers::example precedes main because it is exported (2.3); the declaration of cxx::example itself does not, so it isn't found (basic.lookup.qual.general/3, class.member.lookup/1, and a different part of basic.lookup.general/3) except via the aforementioned replacement.
The wording for the only distantly related using-directive has also changed (as a retroactive Defect Report) to generally allow and give meaning to exported using-directives (basic.lookup.unqual/1, namespace.qual/1, module.interface/4). (They could already be exported in header units.) Of course, if you don’t export them you can use them just for convenience within a module without affecting your clients!
英文:
The strict answer ("the reason that cxx::example
is visible") is that it's an MSVC bug: the declaration of cxx::example
isn't exported, so it isn't found by name lookup outside the module in question ([basic.lookup.general]/2.3, except in certain circumstances involving dependent ADL ([basic.lookup.argdep]/4)).
Since C++20 was published, the wording for using-declarations has been significantly revised to clarify the relationship between the original and new declaration. A using-declaration is not a redeclaration of the original entity or typedef (which is why it can be separately exported); instead, name lookup replaces a using-declarator with the declarations to which it refers ([basic.lookup.general]/3). The using-declaration for cxx::containers::example
precedes main
because it is exported (/2.3); the declaration of cxx::example
itself does not, so it isn't found ([basic.lookup.qual.general]/3, [class.member.lookup]/1, and a different part of [basic.lookup.general]/3) except via the aforementioned replacement.
The wording for the only distantly related using-directive has also changed (as a retroactive Defect Report) to generally allow and give meaning to exported using-directives ([basic.lookup.unqual]/1, [namespace.qual]/1, [module.interface]/4). (They could already be exported in header units.) Of course, if you don’t export them you can use them just for convenience within a module without affecting your clients!
答案2
得分: 1
当前编译器对模块支持的状况不佳。
所以简短的回答是:
预期存在错误和奇怪的现象。
最好支持的编译器是MSVC(_MSC_VER >= 1929)。Clang和gcc都有部分支持。
Clang最近通过这个错误修复来清理和改进了它们的标准兼容性。
问题中的代码似乎展示了与标准匹配的行为。具体来说,导入名称的可见性规则(重点是我的):
在命名空间主体中声明的实体被称为命名空间的成员,这些声明引入的名称在命名空间的声明区域内被称为命名空间的成员名称。命名空间成员名称具有命名空间范围。它的潜在作用域包括从名称的声明点开始的命名空间;对于每个提名成员的using-directive,成员的潜在作用域包括using-directive的潜在作用域部分,该部分跟随成员的声明点。
我认为意图是using
也会导出所使用的部分。
我创建了一个类似的文件集用于测试clang++(1400.0.29.202)。
example.ixx:
export module example;
export namespace cxx {
struct example {
double pi;
};
}
export namespace cxx::containers {
using cxx::example;
}
example.cpp:
import example;
#include <iostream>
int main() {
auto v1 = cxx::containers::example();
auto v2 = cxx::example();
v1.pi = v2.pi = 3.14;
std::cout << v1.pi << std::endl << v2.pi << std::endl;
}
编译:
clang++ -std=c++20 -fmodules-ts --precompile -x c++-module example.ixx -o M.pcm
clang++ -std=c++20 -fmodules-ts -fmodule-file=M.pcm example.cpp -o test
./test
英文:
The current state of module support on compilers is not well.
So the short answer is:
Expect Bugs and Oddities
The best supported compiler is MSVC (_MSC_VER >= 1929). Both clang and gcc are partiality supported.
Clang recently cleaned up and improved their standard compliance in this fix for the bug.
It would appear the code in the question demonstrates behavior that matches the standard. Specifically the visibility rules for imported names (emphasis mine):
> Entities declared in a namespace-body are said to be members of the namespace, and names introduced by these declarations into the declarative region of the namespace are said to be member names of the namespace.
A namespace member name has namespace scope.
Its potential scope includes its namespace from the name's point of declaration on-wards; and for each using-directive that nominates the member's namespace, the member's potential scope includes that portion of the potential scope of the using-directive that follows the member's point of declaration.
T believe the intent is that using
would also export the part used.
I created a similar set of files for clang++ (1400.0.29.202) below that I used to test this.
example.ixx:
export module example;
export namespace cxx {
struct example {
double pi;
};
}
export namespace cxx::containers {
using cxx::example;
}
example.cpp:
import example;
#include <iostream>
int main() {
auto v1 = cxx::containers::example();
auto v2 = cxx::example();
v1.pi = v2.pi = 3.14;
std::cout << v1.pi << std::endl << v2.pi << std::endl;
}
Compile:
clang++ -std=c++20 -fmodules-ts --precompile -x c++-module example.ixx -o M.pcm
clang++ -std=c++20 -fmodules-ts -fmodule-file=M.pcm example.cpp -o test
./test
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论