由于使用声明,字节和模棱两可的符号?
Posted
技术标签:
【中文标题】由于使用声明,字节和模棱两可的符号?【英文标题】:byte and ambiguous symbol due to using declarations? 【发布时间】:2017-12-20 21:53:09 【问题描述】:我们是一个C++ 库。多年来,我们在全局命名空间中有typedef unsigned char byte;
。用户程序和其他库提供了byte
的兼容定义,所以没有问题。
C++17 添加了std::byte
并更改了一个字节的语义。现在我们需要通过避免全局命名空间污染来更加卫生;我们需要将自己与std::byte
隔离开来。我们的改变是将我们的byte
移动到我们的命名空间中。
在测试更改的影响时,我们目睹了意外失败。下面的程序没有遵循最佳实践(根据 Migrating to Namespaces 的 Herb Sutter 所说),但我们希望它是 的典型用例用户程序。
$ cat test2.cxx
#include "cryptlib.h"
#include <iostream>
using namespace std;
using namespace CryptoPP;
// Testing this to select the right byte type
using byte = CryptoPP::byte;
int main(int argc, char* argv[])
CryptoPP::byte block1[16];
std::byte block2[16];
byte block3[16];
return 0;
由于std::byte
、CryptoPP::byte
和using namespace ...
,上述程序具有byte
的竞争定义。正如我所说,我知道它还有一些不足之处。
编译程序会导致(没有未使用的警告):
$ echo $CXXFLAGS
-DNDEBUG -g2 -O3 -std=c++17 -Wall -Wextra
$ g++ $CXXFLAGS test2.cxx ./libcryptopp.a -o test.exe
test2.cxx: In function ‘int main(int, char**)’:
test2.cxx:12:3: error: reference to ‘byte’ is ambiguous
byte block3[16];
^~~~
test2.cxx:6:28: note: candidates are: using byte = CryptoPP::byte
using byte = CryptoPP::byte;
^
In file included from stdcpp.h:48:0,
from cryptlib.h:97,
from test2.cxx:1:
/usr/include/c++/7/cstddef:64:14: note: enum class std::byte
enum class byte : unsigned char ;
^~~~
在编译错误中,让我吃惊的是,我们明确删除了 using byte = CryptoPP::byte;
的歧义。
我们希望建议在全局命名空间中依赖byte
的用户(以及使用using namespace ....
声明的用户)使用using byte = CryptoPP::byte;
,直到他们有时间更新他们的代码。
我的第一个问题是,为什么编译器在通过using
声明被告知使用CryptoPP::byte
后声称byte
是模棱两可的?或者这完全是错误的,我们很幸运我们在编译过程中走了这么远?
第二个相关问题是,在我们将byte
迁移到我们的命名空间后,我们是否可以向用户提供任何建议,以便他们现有的代码能够按预期编译?还是用户修复代码的唯一选择?
我认为这与问题有关:Context of using declaration and ambiguous declaration。 using byte = CryptoPP::byte;
让我感到困惑,因为模棱两可被消除了。
关于下面的 cmets 和 “我认为可以从中吸取教训,从一开始就智能地使用命名空间”,这里有一些背景故事。这是魏岱的Crypto++。它是在 1990 年代初编写的,并且使用了无范围的 byte
,因为 C++ 命名空间不可用。命名空间大约在 5 年后出现。
引入命名空间后,除byte
外,所有内容都移至CryptoPP
。根据source code comments,byte
由于“与其他字节类型定义的歧义” 保留在全局命名空间中。显然,早在 C++17 之前就有了争论。早期的 C++ 编译器可能对这个问题没有帮助。
事后看来,我们应该计划让某些版本的 C++ 这样做(除了糟糕的using namespace ...
交互)。我们应该已经转移到CryptoPP::byte
,并且可能提供了一个无范围的byte
,以便于用户程序适当的警告。
事后诸葛亮总是 20/20。
【问题讨论】:
IMO 更好的解决方案是在任何地方都没有using namespace std;
。
@SomeProgrammerDude - 是的,同意。我们因提出类似建议而受到了一些反击。有些人反对它,因为它实际上意味着图书馆正在为程序制定政策。我也觉得我们不应该替别人做政策决定。另见PR 438, Use ::byte instead of byte。
我们使用 byte = CryptoPP::byte; 明确消除了歧义; ...不,using
声明可用于创建别名。他们的目的不是消除歧义。
using byte = CryptoPP::byte;
只是一个声明。它在全局命名空间中声明了 CryptoPP::byte
的别名,但它不会以任何方式将 byte
的这个版本“提升”到全局命名空间中存在的任何其他版本。
@AnT - [dcl.typedef/1] 的整个“关键字”措辞非常令人困惑。
【参考方案1】:
全局命名空间causes unqualified name lookup to consider all declarations in the nominated namespace as members of the global namespace 中的using-directive。它们与全局命名空间的其他成员处于平等地位,因此向全局命名空间添加额外的声明不会解决非限定查找中的现有歧义。
这样的声明可以解决限定名称查找歧义(例如,byte
在::byte
中的查找),因为that lookup only examines namespaces nominated by using-directives if a declaration is not found。这可能就是你想到的地方。
【讨论】:
我没有看到 OP 说服客户修复他们的代码并使用::byte
而不是 byte
。只不过他们强迫客户使用CryptoPP::byte
。我认为从一开始就明智地使用命名空间可以从中吸取教训。
@StoryTeller 对,如果他们的客户可以使用::byte
,他们就可以使用正确限定的版本。我只是将其包括在内以解决 OP 误解的可能来源。
@T.C.和 StroyTeller - 非常感谢。很抱歉让你们扮演语言律师。【参考方案2】:
我强烈倾向于要求人们明确命名您库的命名空间。 C++ 是关于了解类型,而不是猜测它们。
#include <iostream>
#include <cstddef>
/* simulate CrypoPP for the MVCE */
namespace CryptoPP
using byte = unsigned char;
/* never, ever, ever. People who do this invite their own destruction.
using namespace std;
using namespace CryptoPP;
using byte = CryptoPP::byte;
*/
namespace OurFunkyLibrary
using byte = std::byte; // or CryptoPP::byte, as you wish
int main(int argc, char* argv[])
// explicit
CryptoPP::byte block1[16];
// explicit
std::byte block2[16];
/* if your users really can't stand knowing which type they are using... */
using namespace OurFunkyLibrary;
byte block3[16];
return 0;
【讨论】:
“永远,永远,永远不会这样做” - 哈哈……这正是我们试图设计的情况。我开始认为这个问题没有解决方案,因为使用具有相同符号但语义不同的两个不同名称空间会产生歧义。那些想要这样做的人将不得不接受并修复他们的代码,因为 c++ 似乎不允许我们选择std::byte
或CryptoPP::byte
。
@jww 如果您的用户正在自取灭亡,那么您不应该阻止他们。以上是关于由于使用声明,字节和模棱两可的符号?的主要内容,如果未能解决你的问题,请参考以下文章