2014-10-01 70 views
14

我試圖消除超載設置,如果operator+=丟失超載。SFINAE檢查運營商+ =

我知道如何檢查是否T+T是合法的:

template<typename T, 
     typename CheckTplusT = decltype(std::declval<T>() + std::declval<T>())> 
void foo(T a, T b, ...) 
{ 
    a = a + b; 
} 

但這並不爲+=

template<typename T, 
     typename CheckTplusT = decltype(std::declval<T>() += std::declval<T>())> 
void foo(T a, T b, ...) 
{ 
    a += b; 
} 

工作,這是可以解決的,通過使用內部decltype另一種表達還是需要另一SFINAE構造?

我需要從重載集中消除這個問題的原因是它與另一個接受函數的超載衝突,以作爲+=的替代方案。編譯器是VS2013,gcc4.8

+0

你調用'foo'的什麼形式不起作用? – 2014-10-01 10:22:28

+0

@PiotrS。 :第二種形式不起作用。你不能在右值'std :: declval ()'上調用+ =(非const方法)。但是你可以在rvalues上調用+。比較2 + 2和2 + = 2 – MSalters 2014-10-01 10:38:46

+1

@MSalters如果'+ ='是一種方法,則可以在rvalues上調用它(除非方法有'&'限定符)。問題出在內置的'+ ='和基本類型。 – dyp 2014-10-01 10:52:55

回答

16

我會寫第二種形式爲:

template<typename T> 
auto foo(T a, T b, ...) -> decltype(a+=b, void()) 
{ 
    a += b; 
} 

推導類型decltype(a+=b, void())將只是void如果表達式a+=b是有效的,否則它會導致SFINAE。

那麼,即使在第一種形式,我會使用尾隨返回類型的方法。

+2

接受。由於其他原因(取決於函數參數的返回類型),其餘的重載集合已經需要尾隨返回類型,所以這個解決方案實際上增加了不同重載之間的相似性。 @ gexicide的解決方案更接近我原來的實現,但這對未來的維護者沒有任何好處。 – MSalters 2014-10-01 10:59:10

+0

返回'T&'對於'+ ='語義會更習慣 – TemplateRex 2014-10-01 12:03:42

+0

它應該編譯失敗。這是無效的,因爲A沒有+ =(即使你在Foo的調用中不使用臨時對象) – CashCow 2014-10-01 12:12:38

1

添加這個main()函數:

int main() 
{ 
    int x = 1, y = 2; 
    foo(x, y); 
} 

這是編譯器錯誤是什麼:

main.cpp: In function int main(): main.cpp:15:15: error: no matching 
function for call to foo(int&, int&) 
     foo(x, y); 
      ^main.cpp:15:15: note: candidate is: 
main.cpp:7:6: note: template<class T, class CheckTplusT> void foo(T, T, ...) void 

foo(T a, T b, ...) 
^main.cpp:7:6: note: template argument deduction/substitution failed: 
    main.cpp:6:60: error:  
     using xvalue (rvalue reference) as lvalue 
     typename CheckTplusT = decltype(std::declval<T>() += std::declval<T>())> 

重點線是using xvalue (rvalue reference) as lvalue

這是declval的文檔

此workaro UND工作對我來說:

template<typename T, 
    typename CheckTpluseqT = decltype(*std::declval<T*>() += *std::declval<T*>())> 
void foo(T &a, T b, ...) 
{ 
    a += b; 
} 

int main() 
{ 
    int a = 1, b = 2; 
    foo(a, b); 
    std::cout << a << std::endl; 
} 

輸出3

您也可以使用,當然declval<T&>

+2

它不回答OP的問題。 – ikh 2014-10-01 10:39:56

1

這個怎麼樣?這是在std::declval之前使用的方法。

template<typename T, 
     typename CheckTplusT = decltype(*(T*)nullptr += std::declval<T>())> 
void foo(T a, T b, ...) 
{ 
    a += b; 
    std::cout << "foo with +=" << std::endl; 
} 
+1

'std :: declval ()'更清潔。 – Jarod42 2014-10-01 11:30:03

12

你的+=左側需要一個lvalue但你的解決方案具有的x值。正如dyp在評論中指出的那樣,您可以使用declval<T&>來得到一個左值。這工作正常(只是測試它):

template<typename T, 
     typename CheckTplusT = decltype(std::declval<T&>() += std::declval<T>())> 
void foo(T a, T b, ...) 
{ 
} 
+4

根據函數的細節('a + = b'或'a + = move(b)'),第二個'declval'可能也必須使用'T&'。 – dyp 2014-10-01 10:50:48

+0

我知道'std :: declval ()'會工作,但我仍然*不喜歡*解決方案。它看起來很難看。另外,如果'operator + ='採用非const引用參數,那麼這個解決方案將不起作用。**尾隨返回類型的解決方案更清晰,更好,並且正確*,因爲程序員不會必須處理對象的價值範疇。讓編譯器去做。 – Nawaz 2014-10-01 10:51:06

+3

@Nawaz:對,但它最接近OPs方法。 – gexicide 2014-10-01 10:52:48