Проблема
Если ваши клиенты видят ваши внутренние функции в общедоступных заголовках и если имена этих внутренних универсальных функций являются «общими», то вы можете подвергать своих клиентов риску случайного вызова ваших внутренних универсальных функций.
Например:
namespace Database
{
// internal API, not documented
template <class DatabaseItem>
void
store(DatabaseItem);
{
// ...
}
struct SomeDataBaseType {};
} // Database
namespace ClientCode
{
template <class T, class U>
struct base
{
};
// external API, documented
template <class T, class U>
void
store(base<T, U>)
{
// ...
}
template <class T, class U>
struct derived
: public base<T, U>
{
};
} // ClientCode
int main()
{
ClientCode::derived<int, Database::SomeDataBaseType> d;
store(d); // intended ClientCode::store
}
В этом примере автор main
даже не знает, что Database :: store существует. Он намеревается вызвать ClientCode :: store и становится ленивым, позволяя ADL выбирать функцию вместо указания ClientCode::store
. В конце концов, его аргумент store
исходит из того же пространства имен, что и store
, поэтому он должен просто работать.
Это не работает. Этот пример вызывает Database::store
. В зависимости от внутренних параметров Database::store
этот вызов может привести к ошибке времени компиляции или, что еще хуже, ошибке времени выполнения.
Как исправить
Чем более обобщенно вы называете свои функции, тем более вероятно, что это произойдет. Дайте вашим внутренним функциям (те, которые должны появляться в ваших заголовках) действительно неуниверсальные имена. Или поместите их в подпространство имен, например details
. В последнем случае вы должны убедиться, что ваши клиенты никогда не будут иметь details
в качестве ассоциированного пространства имен для целей ADL. Обычно это достигается путем не создания типов, которые клиент будет использовать, прямо или косвенно, в namespace details
.
Если вы хотите стать более параноиком, начните блокировать вещи с enable_if
.
Если вы думаете, что ваши внутренние функции могут быть полезны для ваших клиентов, то они больше не являются внутренними.
Приведенный выше пример кода не является надуманным. Это случилось со мной. Это случилось с функциями в namespace std
. Я называю store
в этом примере чрезмерно общим . std::advance
и std::distance
являются классическими примерами чрезмерно общего кода. Это то, что нужно остерегаться. И это проблема понятий, которые пытались исправить.