防止C++命名空间中毒的优雅方法

Elegant way to prevent namespace poisoning in C++

本文关键字:方法 中毒 C++ 命名空间 防止      更新时间:2023-10-16

假设 Bob 已将他的库包装到命名空间 "bob" 中,Alice 将通过单个"使用命名空间 bob"使整个命名空间在她自己的函数中可见,而不是为每个项目"使用 bob::XYZ":

// This file is written by Alice:
#include <iostream>
// She uses Bobs library:
#include "bob.hpp"
int main(void) {
    // Import Bobs library and use it:
    using namespace bob;
    unsigned short value = 50000;
    bob::dump_as_signed(value);
    // Should not be possible without std:: prefix:
    cout << "foobar" << endl;
}

另一方面,Bob 试图通过将实现包装在虚拟命名空间中并仅使这些符号可用来防止这种情况,这些符号供其他用户使用:

// This file is written by Bob
#include <iostream>
#include <type_traits>
// Namespace for public use:
namespace bob {
    // Implementation details:
    namespace impl_ {
        // Visible ONLY within sub-namespace:
        using std::cout;
        using std::endl;
        using std::is_integral;
        using std::make_signed;
        // No repeated std:: prefixes at all:
        template <typename T,
            typename S = typename make_signed<T>::type>
        void dump_as_signed(const T i) {
            static_assert(is_integral<T>::value, "no integer");
            // Do something very very useful:
            cout << "signed:" << static_cast<S>(i) << endl;
        }
    }
    // Make available without poisoning with std::*:
    using impl_::dump_as_signed;
}

由于所有 using 指令都包装在 Bobs 主命名空间中的虚拟"impl_"命名空间中,因此 Alice 也不会意外地从 std:: 命名空间导入符号

所以,我的问题是:

  1. 我不喜欢有一个用于实现细节的虚拟命名空间,这对每个人都"理论上"可见。能够使用许多符号的正确方法是什么,例如 std::,而不会泄漏这些符号,并且不会在每个符号前面加上 std::?(我也在考虑生成的API-Docs,它显示"bob::impl_::XYZ"而不是"bob::XYZ"。
  2. 我认为,到处一遍又一遍地重复 std:: a. s. o. 并不是很干燥。我也明白,在更大的范围(例如类)内相对全局的"使用命名空间 std"并不是那么漂亮,但在我看来,数百个 std:: 前缀要丑陋得多。这里哪个方法更好?

假设 Alice 正在使用两个库,分别由 BobCharlie 创建。

// This file is written by Alice:
#include <bob.hpp>
#include <charlie.hpp>
int main(void) {
  using namespace bob;
  using namespace charlie;
  // Do a bunch of stuff
}

现在,查理发明了一个名为foobar的新功能,他将其添加到他的库中。 foobar很棒,他的用户喜欢它。爱丽丝也开始使用它。

然后鲍勃说:"我也喜欢foobar,我想有自己的foobar,我可以在我的图书馆里使用。但我不想依赖查理。所以他创造了自己的版本。

呃哦,现在爱丽丝的代码无法编译!Alice 代码中 foobar 的每一次用法都是模棱两可的,她必须重写整个项目。

然后,同样的事情在下个月发生。之后的下个月。

现在,Alice 的所有客户都非常不满意,因为他们正在构建大型技术并试图保持其依赖项的最新版本,但每次他们尝试升级任何东西时,Alice 的代码都会崩溃。他们在她的错误跟踪器上做了很多错误报告。

爱丽丝给鲍勃和查理发了一封电子邮件,说

伙计们,你们必须停止制作同名的班级,否则我将失去所有的业务!

鲍勃和查理给爱丽丝发了一封电子邮件:

没有爱丽丝,你需要停止在你的代码中放using namespace bob;using namespace charlie;。鲍勃或查理不支持这一点。


现在,让我们再讲一遍同样的故事,除了没有查理。这只是爱丽丝在她的项目中制作自己的类,与鲍勃添加的新名称发生冲突。


简而言之,using namespace指令从来都不是一个好主意(在我看来)。特别是当命名空间是外部库时。你真的不知道这个命名空间在未来会如何变化,如果它以一种对你不利的方式变化,你突然就会手上出现一个巨大的混乱。

使用 namespace = 缩短命名空间通常是一个非常好的主意。我喜欢做以下事情:

namespace my_lib {
namespace qi = boost::spirit::qi;
// Do stuff with qi
// ...
} // end namespace my_lib

这样我就可以在my_lib中使用短名称qi,但我不会强加给我的用户任何东西。(我期望谁不会using namespace my_lib;

如果您是用户,则可以执行以下操作

namespace cha = charlie::name::space::is_way_too_long;

但是,无论您是用户还是库实现者,您应该非常乐意键入短命名空间,例如 bob::std:: ,如果这意味着您的代码在升级库时不会中断。

这与 DRY 无关。在名称上放置某种限定符可以更轻松地阅读代码并理解其含义。

例如,看看SDL,一个流行的C库。据我所知,SDL 中的每个宏SDL_开头,每个函数都以sdl_开头。这是否违反了"DRY"?不。这里没有重复的实现细节 - 通用前缀是为了避免名称冲突。此外,它还使代码更具可读性和可维护性 - 每当我看到一个符号在谈论 SDL 实体时,我就会立即知道。这对人类和计算机都非常有帮助。

using namespace std;using namespace my_lib;就像拿C++最好的功能之一扔进垃圾桶。权衡是,节省自己输入 5 个字符,代价是对可读性和可维护性造成很大损害。


离别想法:using namespace如何影响您收到的错误消息的质量。

这是一个无法编译的简单程序:

#include <iostream>
struct foo {};
int main() {
  std::cout << foo{} << std::endl;
}

当编译器看到此代码时,它将不得不尝试它知道的每个流运算符重载,并检查foo是否可以转换为任何这些内容。因为std::cout是参数之一,所以 ADL 意味着我们必须搜索整个std命名空间。事实证明,令人惊讶的是,foo不能转换为任何这些东西。gcc 5.3我收到以下(200行)错误消息。

main.cpp: In function ‘int main()’:
main.cpp:6:13: error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘foo’)
   std::cout << foo{} << std::endl;
             ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:628:5: note: candidate: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = foo] <near match>
     operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
     ^
/usr/include/c++/5/ostream:628:5: note:   conversion of argument 1 would be ill-formed:
main.cpp:6:20: error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:108:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ostream_type& (*)(std::basic_ostream<_CharT, _Traits>::__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(__ostream_type& (*__pf)(__ostream_type&))
       ^
/usr/include/c++/5/ostream:108:7: note:   no known conversion for argument 1 from ‘foo’ to ‘std::basic_ostream<char>::__ostream_type& (*)(std::basic_ostream<char>::__ostream_type&) {aka std::basic_ostream<char>& (*)(std::basic_ostream<char>&)}’
/usr/include/c++/5/ostream:117:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ios_type& (*)(std::basic_ostream<_CharT, _Traits>::__ios_type&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>; std::basic_ostream<_CharT, _Traits>::__ios_type = std::basic_ios<char>]
       operator<<(__ios_type& (*__pf)(__ios_type&))
       ^
/usr/include/c++/5/ostream:117:7: note:   no known conversion for argument 1 from ‘foo’ to ‘std::basic_ostream<char>::__ios_type& (*)(std::basic_ostream<char>::__ios_type&) {aka std::basic_ios<char>& (*)(std::basic_ios<char>&)}’
/usr/include/c++/5/ostream:127:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(ios_base& (*__pf) (ios_base&))
       ^
/usr/include/c++/5/ostream:127:7: note:   no known conversion for argument 1 from ‘foo’ to ‘std::ios_base& (*)(std::ios_base&)’
/usr/include/c++/5/ostream:166:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(long __n)
       ^
/usr/include/c++/5/ostream:166:7: note:   no known conversion for argument 1 from ‘foo’ to ‘long int’
/usr/include/c++/5/ostream:170:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(unsigned long __n)
       ^
/usr/include/c++/5/ostream:170:7: note:   no known conversion for argument 1 from ‘foo’ to ‘long unsigned int’
/usr/include/c++/5/ostream:174:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(bool) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(bool __n)
       ^
/usr/include/c++/5/ostream:174:7: note:   no known conversion for argument 1 from ‘foo’ to ‘bool’
In file included from /usr/include/c++/5/ostream:638:0,
                 from /usr/include/c++/5/iostream:39,
                 from main.cpp:1:
/usr/include/c++/5/bits/ostream.tcc:91:5: note: candidate: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short int) [with _CharT = char; _Traits = std::char_traits<char>]
     basic_ostream<_CharT, _Traits>::
     ^
/usr/include/c++/5/bits/ostream.tcc:91:5: note:   no known conversion for argument 1 from ‘foo’ to ‘short int’
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:181:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(short unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(unsigned short __n)
       ^
/usr/include/c++/5/ostream:181:7: note:   no known conversion for argument 1 from ‘foo’ to ‘short unsigned int’
In file included from /usr/include/c++/5/ostream:638:0,
                 from /usr/include/c++/5/iostream:39,
                 from main.cpp:1:
/usr/include/c++/5/bits/ostream.tcc:105:5: note: candidate: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char; _Traits = std::char_traits<char>]
     basic_ostream<_CharT, _Traits>::
     ^
/usr/include/c++/5/bits/ostream.tcc:105:5: note:   no known conversion for argument 1 from ‘foo’ to ‘int’
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:192:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(unsigned int __n)
       ^
/usr/include/c++/5/ostream:192:7: note:   no known conversion for argument 1 from ‘foo’ to ‘unsigned int’
/usr/include/c++/5/ostream:201:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long long int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(long long __n)
       ^
/usr/include/c++/5/ostream:201:7: note:   no known conversion for argument 1 from ‘foo’ to ‘long long int’
/usr/include/c++/5/ostream:205:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long long unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(unsigned long long __n)
       ^
/usr/include/c++/5/ostream:205:7: note:   no known conversion for argument 1 from ‘foo’ to ‘long long unsigned int’
/usr/include/c++/5/ostream:220:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(double) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(double __f)
       ^
/usr/include/c++/5/ostream:220:7: note:   no known conversion for argument 1 from ‘foo’ to ‘double’
/usr/include/c++/5/ostream:224:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(float) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(float __f)
       ^
/usr/include/c++/5/ostream:224:7: note:   no known conversion for argument 1 from ‘foo’ to ‘float’
/usr/include/c++/5/ostream:232:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long double) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(long double __f)
       ^
/usr/include/c++/5/ostream:232:7: note:   no known conversion for argument 1 from ‘foo’ to ‘long double’
/usr/include/c++/5/ostream:245:7: note: candidate: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(const void*) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
       operator<<(const void* __p)
       ^
/usr/include/c++/5/ostream:245:7: note:   no known conversion for argument 1 from ‘foo’ to ‘const void*’
In file included from /usr/include/c++/5/ostream:638:0,
                 from /usr/include/c++/5/iostream:39,
                 from main.cpp:1:
/usr/include/c++/5/bits/ostream.tcc:119:5: note: candidate: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__streambuf_type*) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__streambuf_type = std::basic_streambuf<char>]
     basic_ostream<_CharT, _Traits>::
     ^
/usr/include/c++/5/bits/ostream.tcc:119:5: note:   no known conversion for argument 1 from ‘foo’ to ‘std::basic_ostream<char>::__streambuf_type* {aka std::basic_streambuf<char>*}’
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:574:5: note: candidate: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const unsigned char*)
     operator<<(basic_ostream<char, _Traits>& __out, const unsigned char* __s)
     ^
/usr/include/c++/5/ostream:574:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   cannot convert ‘foo{}’ (type ‘foo’) to type ‘const unsigned char*’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:569:5: note: candidate: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const signed char*)
     operator<<(basic_ostream<char, _Traits>& __out, const signed char* __s)
     ^
/usr/include/c++/5/ostream:569:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   cannot convert ‘foo{}’ (type ‘foo’) to type ‘const signed char*’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:556:5: note: candidate: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const char*)
     operator<<(basic_ostream<char, _Traits>& __out, const char* __s)
     ^
/usr/include/c++/5/ostream:556:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   cannot convert ‘foo{}’ (type ‘foo’) to type ‘const char*’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/ostream:638:0,
                 from /usr/include/c++/5/iostream:39,
                 from main.cpp:1:
/usr/include/c++/5/bits/ostream.tcc:321:5: note: candidate: template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const char*)
     operator<<(basic_ostream<_CharT, _Traits>& __out, const char* __s)
     ^
/usr/include/c++/5/bits/ostream.tcc:321:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   cannot convert ‘foo{}’ (type ‘foo’) to type ‘const char*’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:539:5: note: candidate: template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const _CharT*)
     operator<<(basic_ostream<_CharT, _Traits>& __out, const _CharT* __s)
     ^
/usr/include/c++/5/ostream:539:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   mismatched types ‘const _CharT*’ and ‘foo’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:519:5: note: candidate: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, unsigned char)
     operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)
     ^
/usr/include/c++/5/ostream:519:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   cannot convert ‘foo{}’ (type ‘foo’) to type ‘unsigned char’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:514:5: note: candidate: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, signed char)
     operator<<(basic_ostream<char, _Traits>& __out, signed char __c)
     ^
/usr/include/c++/5/ostream:514:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   cannot convert ‘foo{}’ (type ‘foo’) to type ‘signed char’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:508:5: note: candidate: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, char)
     operator<<(basic_ostream<char, _Traits>& __out, char __c)
     ^
/usr/include/c++/5/ostream:508:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   cannot convert ‘foo{}’ (type ‘foo’) to type ‘char’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:502:5: note: candidate: template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, char)
     operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)
     ^
/usr/include/c++/5/ostream:502:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   cannot convert ‘foo{}’ (type ‘foo’) to type ‘char’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from main.cpp:1:
/usr/include/c++/5/ostream:497:5: note: candidate: template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, _CharT)
     operator<<(basic_ostream<_CharT, _Traits>& __out, _CharT __c)
     ^
/usr/include/c++/5/ostream:497:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   deduced conflicting types for parameter ‘_CharT’ (‘char’ and ‘foo’)
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/bits/ios_base.h:46:0,
                 from /usr/include/c++/5/ios:42,
                 from /usr/include/c++/5/ostream:38,
                 from /usr/include/c++/5/iostream:39,
                 from main.cpp:1:
/usr/include/c++/5/system_error:209:5: note: candidate: template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::error_code&)
     operator<<(basic_ostream<_CharT, _Traits>& __os, const error_code& __e)
     ^
/usr/include/c++/5/system_error:209:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   cannot convert ‘foo{}’ (type ‘foo’) to type ‘const std::error_code&’
   std::cout << foo{} << std::endl;
                    ^
In file included from /usr/include/c++/5/string:52:0,
                 from /usr/include/c++/5/bits/locale_classes.h:40,
                 from /usr/include/c++/5/bits/ios_base.h:41,
                 from /usr/include/c++/5/ios:42,
                 from /usr/include/c++/5/ostream:38,
                 from /usr/include/c++/5/iostream:39,
                 from main.cpp:1:
/usr/include/c++/5/bits/basic_string.h:5172:5: note: candidate: template<class _CharT, class _Traits, class _Alloc> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&)
     operator<<(basic_ostream<_CharT, _Traits>& __os,
     ^
/usr/include/c++/5/bits/basic_string.h:5172:5: note:   template argument deduction/substitution failed:
main.cpp:6:20: note:   ‘foo’ is not derived from ‘const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>’
   std::cout << foo{} << std::endl;
                    ^

重点是:如果你做using namespace bob;,那么bob的每一个可流式传输的类型也将出现在该列表中!如果你这样做using namespace charlie;那么他的所有类型也会在那里!

不仅错误消息会更糟,而且您更有可能获得一些您意想不到的非常奇怪的交互。如果鲍勃的类型偶尔可以流式传输到查理的类型之一怎么办?查理的类型偶尔会隐式转换为一些可流式传输的标准类型?

当然,这一切不仅适用于任何运算符重载,也适用于任何模板或函数调用。

因此,最重要的是,如果您避免在一个命名空间中将大量废话混合在一起,C++更容易推理并且效果更好。