2011-10-19 89 views
9

std::forward_list提供了insert_aftererase_after成員,這些成員可能不需要實際訪問std::forward_list對象。因此它們可以作爲static成員函數實現,並在沒有列表對象的情況下被調用 - 對於想要從列表中刪除自己的對象很有用,這是非常常見的用法。 編輯:這種優化只適用於專業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.

用腳註

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.

目前還不清楚我是否加入static將創建一個 「與衆不同」 的成員函數,但除去(隱)論證不應該違反任何添加違約論據的東西,而這是合法的。 (你不能合法地將PTMF帶到任何標準成員函數中。)

它讓我覺得圖書館應該被允許這樣做,但我不確定是否會破壞某些規則。列出的成員函數原型的規範性如何?

+3

變異列表操作需要訪問列表的分配器,所以我懷疑它們可能是靜態的(特別是對於新的有狀態分配器)。 –

+0

儘管如此,模板可以專門用於'std :: allocator'這個非常常見的情況,如果需要的話,用戶也可以自己選擇。 – Potatoswatter

+0

你會如何根據關於* iterator *的知識來專門做這件事?迭代器不知道它屬於哪個列表,也不知道該列表使用哪個分配器。 –

回答

9

標準說如果沒人能分辨出來,你就可以逃脫。而且你無法合法地創建一個PTMF到forward_list,這樣你就很安全。

自定義分配器的危險已經被指出。但即使對於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靜態。

更新

如果你想使「靜態」超載調用,只需讓它稍微比「並不是一成不變的」超載較爲理想。這樣做的一個辦法是改變「不是靜態的」過載:

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(); 
} 

現在測試將顯示出無論是「靜態」或「不是靜態的」,這取決於insert_after成員函數是靜態的還是沒有。

+0

沒想過用戶專用的'std :: allocator',好點。但是,不是''靜態''重載一個模板而沒有可能的良好專業化,因此不合格,不需要診斷? – Potatoswatter

+0

更新答案與全功能「靜態檢測器」。 –

+0

好的...不是很難,但是這與如何通過找到一個非重載成員函數的類型來檢測默認參數有什麼不同?現在模板生成一個有效的特化,但它似乎仍然是UB,因爲表達式'C :: insert_after()'沒有被標準定義。同樣,C++ 03中'Allocator :: construct'不兼容的可變模板實現是因爲它可以被稱爲'construct <>(...)'?對於圖書館規範的深度,這確實是原始問題的焦點。 – Potatoswatter

相關問題