2012-08-16 72 views
19

我已經使用(和見過使用)static_assert到模板的參數值的標誌不期望的值。但是,對於我遇到的所有情況,通過SFINAE禁用這些不需要的值似乎更好,更優雅。例如何時使用`static_assert`而不是SFINAE?

template<typename T, 
     class = std::enable_if<std::is_floating_point<T>::value>::type> 
struct Foo { ... }; 

,而不是

template<typename T> 
struct Foo { 
    static_assert(std::is_floating_point<T>::value, 
       "Foo<T>: T must be floating point :-("); 
    ... 
}; 

所以我的問題:何時使用static_assert,而不是SFINAE,爲什麼?

編輯

我想我已經學會了到目前爲止以下

SFINAE是一個靈活和強大,但可能非常複雜的工具,可用於多種任務,包括函數重載解析(有些似乎認爲它是唯一的目的)。

SFINAE可以以相對簡單的方式被使用過的位置static_assert可以,不同之處在於它出現在聲明(類或函數的),而不是其定義(或是可以插入例如,將一個static_assert轉換爲一個類的前向聲明?)。這使得更逐字,因此更明確的代碼。但是,由於SFINAE比較複雜,因此比簡單的static_assert要難得多。

另一方面static_assert有一個更清晰的編譯器錯誤消息的好處,有些人似乎認爲這是一個主要目的。

+3

你能解釋爲什麼你認爲它是與SFINAE更好嗎? – 2012-08-16 10:02:01

+0

也許我們的答案是以詞彙爲導向的。在你的問題中,我認爲你應該用'std :: enable_if'來代替SFINAE。使用強調事實不是錯誤的機制來產生錯誤聽起來很奇怪。 SFINAE =換人失敗不是一種錯誤 – log0 2012-08-17 12:18:54

回答

6

我覺得static_assert是正確的選擇,如果你想強制執行T是一個浮點類型。此方法比SFINAE解決方案更清楚地表明您的意圖。

+0

我不同意。考慮前向聲明'template class some_class;'。我怎麼能知道,如果'static_assert'只在類定義中出現,那麼這個類只接受整型的模板參數? – Walter 2012-08-16 12:09:28

+0

@Walter SFINAE不適用於這種情況,所以它有什麼關係?在任何情況下,你可以寫一些像'template ...> class some_class;'(其中'Requires'是一個類似於Boost.Concepts中使用的技巧),儘管我不能保證這種事情的合法性(和便攜性)。 – 2012-08-17 05:11:30

7

static_assert使編譯失敗。 SFINAE允許您刪除一個可能的過載。

+0

在所示的情況下,既讓你寫'美孚編譯失敗'。 – 2012-08-16 10:23:32

+0

SFINAE禁用某個模板參數。如果編譯器找不到另一個匹配項,compilition將失敗。 – Walter 2012-08-16 12:10:33

+0

@ R.MartinhoFernandes我剛閱讀*修復enable_if *您的博客/頁,並缺少模板的定義'所有<>'用於幾個條件類型組合。我在哪裏可以找到它? – Walter 2012-08-16 13:05:27

12

您使用SFINAE,如果你想使用另一種過載,並static_assert如果沒有人會適合這樣的參數。

5

爲一體,採用SFINAE可能會導致另一個重載被拾起那原本是一個糟糕的比賽,不會被考慮。

而且在有其他重載的情況,但其中非是可行的,你會得到一些不錯的事情是這樣的:

#include <type_traits> 

void f(int){} 
void f(bool){} 
void f(char){} 
void f(float){} 
void f(long){} 
void f(double){} 
void f(short){} 
void f(unsigned){} 
void f(void*){} 
void f(void (*)()){} 

template<class C, class T = int> 
using EnableIf = typename std::enable_if<C::value, T>::type; 

template<class T> 
struct sfinae_false : std::false_type{}; 

template<class T> 
void f(T&&, EnableIf<sfinae_false<T>> = 0){} 

int main(){ struct X{}; f(X()); } 

輸出:

source.cpp: In function 'int main()': 
source.cpp:23:30: error: no matching function for call to 'f(main()::X)' 
source.cpp:23:30: note: candidates are: 
source.cpp:3:6: note: void f(int) 
source.cpp:3:6: note: no known conversion for argument 1 from 'main()::X' to 'int' 
source.cpp:4:6: note: void f(bool) 
source.cpp:4:6: note: no known conversion for argument 1 from 'main()::X' to 'bool' 
source.cpp:5:6: note: void f(char) 
source.cpp:5:6: note: no known conversion for argument 1 from 'main()::X' to 'char' 
source.cpp:6:6: note: void f(float) 
source.cpp:6:6: note: no known conversion for argument 1 from 'main()::X' to 'float' 
source.cpp:7:6: note: void f(long int) 
source.cpp:7:6: note: no known conversion for argument 1 from 'main()::X' to 'long int' 
source.cpp:8:6: note: void f(double) 
source.cpp:8:6: note: no known conversion for argument 1 from 'main()::X' to 'double' 
source.cpp:9:6: note: void f(short int) 
source.cpp:9:6: note: no known conversion for argument 1 from 'main()::X' to 'short int' 
source.cpp:10:6: note: void f(unsigned int) 
source.cpp:10:6: note: no known conversion for argument 1 from 'main()::X' to 'unsigned int' 
source.cpp:11:6: note: void f(void*) 
source.cpp:11:6: note: no known conversion for argument 1 from 'main()::X' to 'void*' 
source.cpp:12:6: note: void f(void (*)()) 
source.cpp:12:6: note: no known conversion for argument 1 from 'main()::X' to 'void (*)()' 
source.cpp:21:6: note: template<class T> void f(T&&, EnableIf<sfinae_false<T> >) 
source.cpp:21:6: note: template argument deduction/substitution failed: 
+2

SFINAE的令人討厭的屬性是,你需要確保多重過載有不重疊的條件,否則你仍然會得到一個錯誤。我非常喜歡標籤調度選擇性重載。 – TemplateRex 2012-08-16 10:20:13

+1

鑑於在這個問題的例子是一個類模板而不是函數模板,確實給這個答案量「你是班正確的,但是有沒有考慮功能」?或者靜態斷言對類也有優勢嗎? – 2012-08-16 10:31:53

+0

@SteveJessop:那麼,SFINAE並不適用於類模板,因爲它*是一個硬性錯誤(在'std :: enable_if '中沒有名爲'type'的成員...)。 – Xeo 2012-08-16 10:39:48

相關問題