2011-10-19 9 views
9

std::forward_listは、insert_afterおよびerase_afterのメンバーを提供し、これらのメンバーは実際にオブジェクトにアクセスする必要がない場合があります。したがって、それらはメンバー関数としてstaticのように実装でき、リストオブジェクトなしで呼び出すことができます。リストからそれ自身を削除したいオブジェクトにとっては非常に一般的です。 EDIT:この最適化は std::allocatorの特殊化またはユーザー定義のステートレスアロケータにのみ適用されます。std :: forward_listメンバーは静的として実装できますか?

標準に準拠した実装でこれを行うことはできますか?

§17.6.5.5/ 3

A call to a member function signature described in the C++ standard library behaves as if the implementation declares no additional member function signatures.

はそれがstaticを追加すると "異なる" メンバ関数を作成するかどうか私には明確ではないですが、(暗黙の除去脚注

A valid C++ program always calls the expected library member function, or one with equivalent behavior. An implementation may also define additional member functions that would otherwise not be called by a valid C++ program.

ながら言います)引数は、既定の引数を追加することができないものを壊してはならず、それは合法です。 (あなたは合法的に標準的なメンバ関数にPTMFをとることはできません)

ライブラリはこれを許可しなければなりませんが、いくつかのルールが破られるかどうかはわかりません。リストされたメンバ関数のプロトタイプはどのくらい規範的ですか?

+3

リストの操作を変更するには、リストのアロケータにアクセスする必要があるため、静的になる可能性はありません(特に、新しいステートフルなアロケータを使用すると)。 –

+0

しかし、テンプレートは、非常に一般的な 'std :: allocator'の場合に特化することができ、必要に応じてユーザ自身が同様に特殊化することができます。 – Potatoswatter

+0

* iterator *に関する知識に基づいて、これをどのように特化しますか?イテレータは、それがどのリストに属しているか、どのリストが使用するアロケータでもわからない。 –

答えて

9

誰もが違いを感じることができなければ、標準ではそれを取り除くことができます。あなたは正しい方法で、forward_listにPTMFを合法的に作成できないので、あなたはそう安全です。

カスタムアロケータの危険性は既に指摘されています。しかし、std::allocator<T>の場合でも、誰かがstd::allocator<MyType>を特化して、allocator::construct/destroyが呼び出されていないことを検出する危険性があります。

いいえ、std::forward_list<int>(カスタムアロケータはありません、ユーザー定義のvalue_typeはありません)を専門とし、insert_afterを静的にすることはできますか?

いいえこの変更は、新しいSFINAE機能で検出可能です。このプログラムの実行は

#include <memory> 
#include <iostream> 

template <class T, class A = std::allocator<T>> 
class forward_list 
{ 
public: 
    typedef T value_type; 
    struct const_iterator {}; 
    struct iterator {}; 

    iterator insert_after(const_iterator p, const T& x); 
}; 

template <class C> 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    -> decltype(C::insert_after(p, x)) 
{ 
    std::cout << "static\n"; 
    return typename C::iterator(); 
} 

template <class C> 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    -> decltype(c.insert_after(p, x)) 
{ 
    std::cout << "not static\n"; 
    return typename C::iterator(); 
} 

int main() 
{ 
    ::forward_list<int> c; 
    test(c, ::forward_list<int>::const_iterator(), 0); 
} 

と出力します:ここでデモがある

not static 

しかし、私はinsert_afterが静的​​にする場合:

static iterator insert_after(const_iterator p, const T& x); 

それから私は、コンパイル時にエラーが表示されます。

test.cpp:34:5: error: call to 'test' is ambiguous 
    test(c, ::forward_list<int>::const_iterator(), 0); 
    ^~~~ 
test.cpp:16:6: note: candidate function [with C = forward_list<int, std::__1::allocator<int> >] 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    ^
test.cpp:24:6: note: candidate function [with C = forward_list<int, std::__1::allocator<int> >] 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    ^
1 error generated. 

差異が検出されました。

したがって、これを静的にすることは不適です。forward_list::insert_after

更新

あなたが呼び出し可能な「静的」な過負荷を作りたい場合は、単に少しより望ましい「静的でない」オーバーロードよりも、それをする必要があります。今テストはinsert_afterメンバ関数が静的であるかどうかに応じて、いずれかの「静的」または「静的でない」プリントアウトされます

template <class C, class ...Args> 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x, Args...) 
    -> decltype(c.insert_after(p, x)) 
{ 
    std::cout << "not static\n"; 
    return typename C::iterator(); 
} 

:それを行う方法の1つは「静的ではない」過負荷を変化させています。

+0

ユーザー特化型の 'std :: allocator'、素晴らしい点は考えていませんでした。しかし、 ''静的な ''オーバーロードはテンプレートのオーバーフォーロードではありません。 – Potatoswatter

+0

完全に機能する「静的検出器」を使って答えを更新しました。 –

+0

大丈夫です...難しくありませんが、オーバーロードされていないメンバー関数のタイプを見つけることによってデフォルトの引数を検出するなどとはどう違うのでしょうか?テンプレートは有効な特殊化を生成しますが、 'C :: insert_after()'という式は標準で定義されていないため、まだUBのようです。同様に、 'struct <>(...)'として呼び出すことができるので、C++ 03では 'Allocator :: construct'のバリデーションテンプレート実装は非準拠ですか?これは実際には、ライブラリの仕様の深さに関する元の質問のジストです。 – Potatoswatter

関連する問題