问题
如果您的客户可以在公共标头中看到您的内部函数,并且如果这些内部通用函数的名称是“通用的”,那么您可能会让您的客户面临意外调用您的内部通用函数的风险。
例如:
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
是过度通用代码的经典示例。这是需要防范的。这是一个试图解决的问题概念。