我们假设Alice正在使用两个库,由Bob and Charlie.
// 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 的所有客户都非常不高兴,因为他们正在构建大型技术并试图保持其依赖项的最新版本,但每次他们尝试升级任何内容时,Alice 的代码都会崩溃。他们在她的错误跟踪器上做了很多错误报告。
爱丽丝向鲍勃和查理发送了一封电子邮件并说
伙计们,你们必须停止用相同的名字制作课程,否则我会失去我所有的生意!
鲍勃和查理给爱丽丝发了一封电子邮件:
不,爱丽丝,你需要停止投入using namespace bob;
and using namespace charlie;
在你的代码中。鲍勃或查理不支持这一点。
现在,让我们再讲一遍同样的故事,只不过没有查理。只是爱丽丝在她的项目中创建了自己的类,与鲍勃添加的新名称发生了冲突。
简而言之,一个using namespace
指令从来都不是一个好主意(在我看来)。尤其当命名空间是外部库时。你不really知道该名称空间将来会如何变化,如果它的变化对您来说完全不利,那么您的手上就会突然陷入一团糟。
Using namespace =
to shorten命名空间通常是一个非常好的主意。我喜欢做以下事情:
namespace my_lib {
namespace qi = boost::spirit::qi;
// Do stuff with qi
// ...
} // end namespace my_lib
这样我就可以使用简称qi
in my_lib
,但我不会向我的用户强加任何东西。 (我期望谁会not正在做using namespace my_lib;
!)
如果您是用户,您可以执行类似的操作
namespace cha = charlie::name::space::is_way_too_long;
但是,您应该非常乐意键入短名称空间,例如bob::
or std::
,无论您是用户还是库实现者,这是否意味着您的代码在库升级时不会中断。
这与 DRY 无关。推杆some名称上的某种限定符可以让您更轻松地阅读代码并理解其含义。
例如,SDL,一个流行的 C 库。据我所知,everySDL 中的宏开始SDL_
and every函数开始sdl_
。这是否违反了“DRY”?不。这里没有重复的实现细节——公共前缀是为了避免名称冲突。此外,它还使代码更具可读性和可维护性——每当我看到一个正在谈论 SDL 实体的符号时,我立即就知道了。它对人类和计算机都非常有帮助。
Putting using namespace std;
or 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;
那么他所有的类型也会在那里!
不仅错误消息会更糟糕,而且您更有可能得到一些您没有预料到的非常奇怪的交互。如果鲍勃的类型偶尔会流式传输到查理的类型之一怎么办? Charlie 的类型有时可以隐式转换为某种可流式传输的标准类型?
当然,这一切不仅适用于任何运算符重载,还适用于任何模板或函数调用。
因此,最重要的是,如果您避免在一个名称空间中混合大量垃圾,C++ 会更容易推理并且工作得更好。