2014-11-05 109 views
0

假設我們有這樣的事情:模板強制執行的方法防止隱式轉換

template <class B> 
class A 
{ 
    void Foo(B& b) 
    { 
     b.Bar(0.5); 
    } 
}; 

class B 
{ 
    void Bar(float) {} 
    void Bar(double) {} 
    void Bar(int) {} 
}; 

在這段代碼中,類型B必須提供一種方法Bar()這需要一些整數類型的參數。問題是在這裏所有3個版本的B::Bar()都是允許的。有沒有辦法只允許這些方法的一個版本,例如,只有在B提供了Bar(float)時才能編譯?

+0

爲什麼B本身沒有模板? – NaCl 2014-11-05 23:01:41

+0

那麼,爲了使事情順利,你想要在A?在我看來,你需要兩種不同的東西 - 可以檢查模板參數'B'是否有一個帶有浮點的方法'Bar'。但是,這本身不會阻止像'b.Bar(0);'這樣的東西。你能澄清嗎? – jrok 2014-11-05 23:08:16

+0

你正在將字面值傳遞給'Bar'調用,我沒有看到你想讓編譯器爲你做的事情,你不能自己做。 – Aesthete 2014-11-05 23:14:15

回答

1

你可以使用這個(恐怖)技術,這將導致如果A進行實例化一個類型B不具有公共void Foo(float)成員,試圖通過採取特定指針到成員類型的編譯失敗它。

template <class B> 
class A 
{ 
public: 
    void Foo(B& b) 
    { 
     static_cast<void (B::*)(float)>(&B::Bar); 

     b.Bar(0.5); 
    } 
}; 

Demo of a resulting compilation failure

如果你想,雖然,那麼你需要使用b.Bar(0.5f);實際調用此方法。 0.5是一個double字面值,而不是float字面值,所以您需要檢查以確保它具有正確的成員,但如果它有void Bar(double),則無論如何您都會這麼稱呼它。將常量改爲0.5f可以解決這個問題。

請注意,由於採取指針沒有副作用,並且不使用結果,所以任何像樣的編譯器都會將其優化爲空操作。


你也可以去傳統SFINAE路線像這樣的東西:

template <typename T, typename TMethod> 
class has_bar_method 
{ 
private: 
    struct yes { char _; }; 
    struct no { char _[2]; }; 
    template <typename U, TMethod = &U::Bar> 
    static yes impl(U*); 
    static no impl(...); 

public: 
    enum { value = sizeof(impl(static_cast<T*>(nullptr))) == sizeof(yes) }; 
}; 

像這樣來使用:

void Foo(T& b) 
{ 
    static_assert(has_bar_method<T, void (T::*)(float)>::value, 
        "T has method void Bar(float)"); 

    b.Bar(0.5f); 
} 

現在,如果模板失敗實例,我們得到一個不錯的解釋原因的訊息:

prog.cpp:25:8: error: static assertion failed: T has method void Bar(float)

Demo

+0

標記爲答案。你可以評論爲什麼你的第一個建議是可怕的嗎? – user1715925 2014-11-06 04:16:36

+1

@ user1715925本身,鑄件看起來沒有必要,在將來的維護過程中可能會被意外移除。當然,你可以通過添加註釋來解釋它爲什麼會存在。但是,它產生的錯誤信息不是很有幫助。 'static_assert'可以讓你指定一個自定義的錯誤消息,它更可能清楚地表達問題。 – cdhowie 2014-11-06 04:27:08

+0

謝謝,你是否介意在答案中提出這個問題?它可能會幫助其他人 – user1715925 2014-11-06 05:09:56