我们是 C ++库 。多年来,我们在全局命名空间中有typedef unsigned char byte;
。用户程序和其他库提供了byte
的兼容定义,因此没有问题。
C ++ 17添加了std::byte
并更改了一个字节的语义。现在,我们需要通过避免全球名称空间污染来提高卫生水平;我们需要将自己与std::byte
隔离开来。我们的更改是将byte
移到我们的命名空间中。
我们正在目睹意外失败,因为我们测试了变化的影响。 下面的程序 遵循最佳做法(根据Herb Sutter Migrating to Namespaces的说法),但我们希望它是 <的典型用例em>用户程序 。
$ 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
,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 {};
^~~~
在编译错误中,让我感到惊讶的是,我们明确地删除了using byte = CryptoPP::byte;
的含糊不清。
我们希望建议依赖全局命名空间byte
(以及使用using namespace ....
声明的用户)的用户使用using byte = CryptoPP::byte;
,直到他们有时间更新代码。
我的第一个问题是,为什么声明byte
的编译器在被告知通过CryptoPP::byte
声明使用using
之后是不明确的?或者这是完全错误的,我们很幸运,我们在编译过程中走得那么远?
第二个相关问题是,我们可以向用户提供任何建议,以便在我们将byte
迁移到命名空间后,他们的现有代码按预期编译吗?或者是用户修复代码的唯一选择?
我认为这与问题有关:Context of using declaration and ambiguous declaration。 <{1}}因为模糊性被删除而绊倒了我。
关于以下评论和“我认为从这里可以学到一些关于从一开始就明智地使用名字的经验教训”,这里有一些背景故事。这是魏岱的Crypto++。它是在20世纪90年代早期编写的,并使用了无范围的using byte = CryptoPP::byte;
,因为C ++命名空间不可用。名称空间大约在5年后出现。
引入名称空间后,除byte
外,所有内容都已移至CryptoPP
。根据{{3}},byte
由于“与其他字节类型的歧义”而保留在全局命名空间中。显然,在C ++ 17之前很久就存在争议。早期的C ++编译器可能没有帮助解决这个问题。
事后看来,我们应该计划在某些版本的C ++中执行此操作(除了错误的byte
交互)。我们应该已经转移到using namespace ...
,并且可能提供了一个无范围的CryptoPP::byte
,以方便用户程序并提供适当的警告。
后见之明总是20/20。
答案 0 :(得分:6)
全局命名空间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。这可能是你有了这个主意的地方。
答案 1 :(得分:1)
我非常倾向于要求人们明确命名您的库的命名空间。 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;
}