我们是一个C++库。多年来,我们在全局命名空间中使用了typedef unsigned char byte;
。用户程序和其他库提供了兼容的byte
定义,因此没有问题。
C++17添加了std::byte
并更改了字节的语义。现在,我们需要更加卫生,避免全局名称空间污染;我们需要将自己与std::byte
隔离开来。我们的改变是将byte
移动到我们的命名空间中。
当我们测试变化的影响时,我们正在目睹一个意外的失败。下面的程序不遵循最佳实践(根据迁移到命名空间的Herb ),但我们希望它是用户程序的典型用例。
$ 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;
}
由于byte
、CryptoPP::byte
和using namespace ...
,上述程序具有与之相竞争的定义。就像我说的,我知道它留下了一些有待改进的地方。
编译程序会导致(没有未使用的警告):
$ 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;
,直到他们有时间更新他们的代码为止。
我的第一个问题是,为什么编译器在被告知通过byte
声明使用CryptoPP::byte
之后才声称using
是模糊的?或者这完全是错误的,而且我们很幸运我们在编译过程中取得了如此大的进展?
第二个相关问题是,在我们将byte
迁移到名称空间后,我们是否可以向用户提供任何建议,以便他们的现有代码按预期的方式进行编译?或者是用户修复他们的代码的唯一选择?
我认为这与问题有关:使用声明和歧义声明的上下文。自从模糊度消除后,using byte = CryptoPP::byte;
就把我绊倒了。
关于下面的评论和“我认为这是一个关于从入门中明智地使用名称的经验教训”,有一些背景故事。这是魏代的Crypto++。它是在20世纪90年代早期编写的,使用了非作用域byte
,因为C++名称空间不可用。名称空间大约在5年后出现。
当引入名称空间时,除了CryptoPP
之外,所有内容都被移动到byte
中。根据源代码注释的说法,由于“与其他字节类型防御不明确”,byte
仍然处于全局命名空间中。显然,早在C++17之前就存在争议,早期的C++编译器可能没有解决这个问题。
事后看来,我们应该计划使用C++的某些版本(除了糟糕的using namespace ...
交互之外)。我们应该已经转移到CryptoPP::byte
,并可能提供了一个非作用域的byte
,以便为用户程序提供适当的警告。
后知后觉总是20/20。
发布于 2017-07-16 09:19:41
全局命名空间导致非限定名称查找将指定命名空间中的所有声明视为全局命名空间的成员。中的使用指令。它们与全局命名空间的其他成员处于同等地位,因此向全局命名空间添加额外的声明不会解决非限定查找中存在的歧义。
这样的声明可以解决限定名称查找的歧义(例如,byte
在::byte
中的查找),因为如果找不到声明,则该查找只检查_using_s指定的命名空间。。那可能就是你想出来的地方。
发布于 2017-07-16 10:59:57
我强烈要求人们明确地命名库的名称空间。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;
}
https://stackoverflow.com/questions/45125957
复制相似问题