jww*_*jww 4 c++ scope namespaces c++17
我们是一个C++库.多年来我们一直typedef unsigned char byte;在全局命名空间中.用户程序和其他库提供了兼容的定义byte,因此没有问题.
C++ 17添加std::byte并更改了一个字节的语义.现在我们需要通过避免全局名称空间污染来提高卫生水平; 我们需要隔离自己std::byte.我们的改变是将我们byte带入命名空间.
当我们测试变化的影响时,我们正在目睹意外的失败.下面的程序没有遵循最佳实践(根据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;
}
Run Code Online (Sandbox Code Playgroud)
上面的程序有相互竞争的定义byte因std::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 {};
^~~~
Run Code Online (Sandbox Code Playgroud)
在编译错误中,让我感到惊讶的是,我们明确地删除了歧义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由于"与其他字节typedef模糊",因此仍保留在全局命名空间中.显然,在C++ 17之前很久就存在争议.早期的C++编译器可能没有帮助解决这个问题.
事后看来,我们应该计划在某些版本的C++中做这个(除了糟糕的using namespace ...交互).我们应该已经移动到CryptoPP::byte,并且可能byte为了方便用户程序提供了一个没有方便的警告.
后见之明总是20/20.
一个using指令在全局命名空间导致不合格的名称查找考虑提名的命名空间中的所有声明的全局命名空间的成员.它们与全局命名空间的其他成员保持平等,因此向全局命名空间添加其他声明将无法解决非限定查找中的现有歧义.
此类声明可以解析限定名称查找歧义(例如,查找bytein ::byte),因为该查找仅检查由using指令 s 指定的名称空间(如果未找到声明).那可能就是你有了这个主意的地方.
我强烈倾向于要求人们明确命名您的库的名称空间。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;
}
Run Code Online (Sandbox Code Playgroud)