問題
クライアントがパブリック ヘッダーで内部関数を確認でき、これらの内部ジェネリック関数の名前が「共通」である場合、クライアントを誤って内部ジェネリック関数を呼び出す危険にさらしている可能性があります。
例えば:
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
守るべきものです。そして、それは概念が修正しようとしている問題です。