C ++ 11 — элегантный способ предотвратить отравление пространства имен в переполнении стека

Давайте предположим, боб обернул свою библиотеку в пространство имен «Боб», а также Алиса собирается сделать все пространство имен видимым внутри ее собственной функции одним «используя пространство имен Боб», вместо «используя 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;
}

С другой стороны, Боб пытался предотвратить такие сценарии оборачивая реализацию в пустое пространство имен, и делая доступны только эти символы, которые предназначены для других пользователей:

// 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;

}

Поскольку все директивы использования заключены в фиктивное пространство имен «impl_» в основном пространстве имен Бобса, Алиса не рискует случайно импортировать символы из пространства имен std ::, тоже.

Итак, мои вопросы:

  1. Мне не нравится, что существует пустое пространство имен для деталей реализации, которое «теоретически» видно всем. Есть ли лучший способ, чтобы иметь возможность использовать много символов для e. г. std :: без утечки этих AND без префикса каждого символа, явного с std ::? (Я также думаю о сгенерированных API-документах, которые показывают «bob :: impl _ :: XYZ» вместо «bob :: XYZ».)
  2. Я думаю, это не очень СУХОЙ, чтобы повторить std :: a. s. о. снова и снова везде. Я также понимаю, что относительно глобальное «использование пространства имен std» внутри более широкой области видимости (например, класса) не так уж и прекрасно, но, на мой взгляд, сотни префиксов std :: гораздо страшнее. Помимо проблем с отравлением: Как вы думаете, что лучше и почему? Или получил совершенно другую идею?

Хорошо, я надеюсь, что мой вопрос понятен. Спасибо за прочтение! 🙂

1

Решение

Давайте предположим, что Алиса использует две библиотеки, сделанные боб а также Чарли.

// 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 Я могу использовать в моей библиотеке. Но я не хочу зависеть от Чарли «. Поэтому он создает свою собственную версию.

Ой, теперь код Алисы не компилируется! Каждое использование foobar в коде Алисы неоднозначно и она должна переписать весь свой проект.

Затем то же самое происходит в следующем месяце. И через месяц после этого.

Теперь все клиенты Алисы действительно недовольны тем, что они создают крупные технологии и стараются поддерживать актуальные версии своих зависимостей, но каждый раз, когда они пытаются что-то обновить, код Алисы вырывается. Они делают много сообщений об ошибках на ее трекере ошибок.

Алиса отправляет электронное письмо Бобу и Чарли и говорит

Ребята, вы должны прекратить делать уроки с одинаковыми именами, или я потеряю все свои дела!

Боб и Чарли отправляют электронное письмо Алисе:

Нет, Алиса, ты должен прекратить ставить 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

Таким образом, я могу использовать короткое имя qi в my_lib, но я ничего не навязываю своим пользователям. (Кого я ожидаю, будет не делать using namespace my_lib;!)

Если вы пользователь, вы можете сделать что-то вроде

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

Но вы должны быть более чем счастливы вводить короткие пространства имен, такие как bob:: или же std::являетесь ли вы пользователем или разработчиком библиотеки, если это будет означать, что ваш код не сломается при обновлении библиотек.

Это не о СУХОЙ. Ввод немного своего рода классификатор имен упрощает чтение кода и понимание его значения.

Например, посмотрите на SDL, популярную библиотеку C. Насколько мне известно, каждый начинается макрос в SDL SDL_ а также каждый функция начинается sdl_, Это нарушение «СУХОЙ»? Нет. Здесь нет дублирующихся подробностей реализации — здесь есть общий префикс, чтобы избежать коллизий имен. Кроме того, это делает код более читабельным и понятным — всякий раз, когда я вижу символ, который говорит о сущности 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 ++ гораздо проще рассуждать и работает намного лучше, если вы избегаете смешивать много дерьма в одном пространстве имен.

5

Другие решения

Других решений пока нет …