2011-08-28 84 views
2

打字有沒有辦法做這樣的事情在下面的C++C++編譯時鴨接口

template<typename TAnimal> 
bool can_eat(TAnimal& animal) where bool TAnimal::IsAlive() exists 
{ 
    return !animal.IsAlive(); 
} 

//... 

Duck duck; 
assert(can_eat(duck) == true); //compiles 

Wood wood; 
can_eat(wood); // fails to compile because wood doesn't have IsAlive() 

顯式接口,在我看來,使它更加清晰的功能所期待的。但是,我不想創建一個實際的接口類(非常乏味)。

+1

你可以設置某種typetrait來測試類是否有給定的成員函數,但是如果你嘗試傳遞一個無效的模板參數,那麼你得到的所有東西都是編譯時錯誤,案子。那麼爲什麼要麻煩。 –

+2

我不明白使用接口類的問題是什麼...... –

+0

出於興趣,爲什麼這需要是模板方法? –

回答

8

不是使用enable_if來執行您的要求。使用enable_if將使函數'消失',這可能會讓用戶感到困惑。典型症狀是錯誤消息,如error: no matching function for call to expression。這並沒有向用戶確切地傳達違反了要求。

您應該改爲使用static_assert來強制您的要求,假設C++ 0x。如果您使用的是C++ 03,那麼您是否應該使用static_assert(例如Boost的STATIC_ASSERT)的仿真是一種折騰,因爲這通常意味着交換另一個錯誤消息。

對比度:

// SFINAE for types that do not decay to int 
template< 
    typename T 
    , typename = typename std::enable_if< 
     std::is_same< 
      typename std::decay<T>::type 
      , int 
     >::value 
    >::type 
> 
void 
f(T&&) 
{} 

// using static assert instead 
template< 
    typename T 
> 
void 
g(T&&) 
{ 
    static_assert(std::is_same<typename std::decay<T>::type, int>::value 
       , "Constraints violation"); 
} 

使用GCC做f("violation")我得到以下錯誤(這兩個消息都與文件名和行號):

error: no matching function for call to 'f(const char [10])' 

在另一方面,g("violation")產量:

error: static assertion failed: "Constraints violation" 

現在想象你使用清晰明確的消息n你的斷言,如模板foo內部的foo: parameter type must be CopyConstructible。因此,SFINAE和static_assert有點對立,因此既有明確的約束違規消息,又有巧妙的過載並不總是可能的和/或容易的。


你想要做的事情很容易實現使用Boost.ConceptCheck。但是它需要寫出非線性代碼:約束類。我也不認爲它在可用的地方使用static_assert,所以錯誤消息可能不會那麼好。這可能會在未來發生變化。

另一種可能性是使用static_assert +類型特徵。這種方法的有趣之處在於,使用C++ 0x時,庫帶有一系列有用的特性,您可以直接使用,而無需編寫外部代碼。更有意思的是,特徵的使用並不侷限於書寫限制,它們也可以與SFINAE一起使用來製造聰明的過載。

但是,沒有可用的特徵來檢查類型是否支持特定的操作成員,可能是由於C++處理函數名稱的方式。我們不能使用像has_member<T, &T::member_to_test_for>之類的東西,因爲只有當我們測試的成員首先存在時纔會有意義(忽略重載等事情以及我們還需要將成員的簽名傳遞給特徵的事實) 。

下面是如何轉換任意表達式爲特徵:

template<typename T> 
struct void_ { 
    typedef void type; 
}; 

template<typename T> 
struct trait { 
private: 
    typedef char yes[1]; 
    typedef char no[2]; 

    template<typename U> 
    static 
    yes& 
    test(U&& 
     , typename void_<decltype(std::declval<U&>().member())>::type* = 0); 

    static 
    no& 
    test(...); 

public: 
    static constexpr bool value = sizeof test(std::declval<T>()) == sizeof(yes); 
}; 

注意如何相當大的,這是。編寫一個Boost.ConceptCheck約束類可能更容易(但記住,不可重複使用SFINAE)。

任意表達式是std::declval<U&>().member()。在這裏,要求是給定的左值參考U(或T對於特徵爲真的情況(如果您願意的話)),那麼調用member()就是有效的。

您還可以檢查出表達式的類型(即任何的member超載已經提取了該表達式的結果類型)可轉換爲A型(不檢查是否那種類型,這太限制沒有很好的理由)。然而,這會使這個特性膨脹,再次使這個變得更受限制。

我不知道如何使static_assert函數模板的簽名的一部分(這似乎是你想要的東西),但它可以出現在類模板中。 Boost.ConceptCheck也不支持這一點。

+0

好的一般介紹。我更喜歡它,如果game_(Concepts)的_name稍早提到,雖然 – sehe

+0

@sehe啊;但這不是概念,而是次佳。我沒有解決這些問題,因爲另一個答案提到他們。一個關鍵的區別是,C++ 0x概念檢查/ SFINAE最好根據有效表達式來完成。如果我理解正確,概念就處於更高層次。 –

0

你不需要做任何事情 - 只要在你的例子中省略假設的「where ... exists」,它就是正常的C++代碼。

如果你堅持只有某些條件下的功能是可用的,你可以嘗試用has_member從這裏結合boost::enable_ifhttp://lists.boost.org/Archives/boost/2002/03/27229.php

的想法是,你會只允許模板功能進行,如果一些條件實例遇到了......但由於SFINAE編譯器基本上已經在爲條件與該函數的實際編譯時需求(如您的示例中)相同的情況下爲您執行該操作。

1
where void TAnimal::IsAlive() exists 

我假設你的意思是bool TAnimal::IsAlive()?如果是這樣,C++已經做了你所要求的。如果鴨子進行IsAlive()方法,那麼這將編譯:

Duck duck; 
assert(can_eat(duck) == true); //compiles 

如果伍德不具備的IsAlive()方法,這不會編譯:

Wood wood; 
can_eat(wood); // fails to compile because wood doesn't have IsAlive() 

這就是你在問什麼對嗎?

+0

對不起,應該是bool ... – jameszhao00

+0

感謝您的回答。有沒有辦法將這些需求放在一個地方(例如在函數decl處),而不是在整個代碼中進行intersparsed? – jameszhao00

+1

當然,但海事組織比它的價值更麻煩。你需要一個類型特徵,並且你必須爲每個需要測試的方法定義一個不同的特徵,你不能只有一個處理它們的通用方法。查看[這個問題]的答案(http://stackoverflow.com/questions/257288/is-it-possible-to-write-ac-template-to-check-for-a-functions-existence),找到了解如何做到這一點。 –

5

這是概念打算解決的事情。

概念是對最新C++標準的增加,但因爲委託人不相信它們已經足夠穩固,不能包含在語言中。 See what Herb Sutter wrote關於他們被排除在標準之外。

從技術上講,概念是不必要的,因爲模板只是使用任何他們可以(即丟失where條款,並且你有你所要求的)。如果在編譯時所需的方法不存在,那麼代碼就不會編譯。但是概念會給編碼器更加明確地控制類型的接口,並且會給出比大多數編譯器當前提供的更合理的錯誤消息。

0

正如其他人所說,這隻會工作。如果函數不存在,它將無法實例化模板。

boost庫包含一些類來協助這種事情,例如enable_if它可以用來在條件爲真的情況下僅「啓用」模板。還有一些相關的type traits庫,您可能可以使用this在編譯時確定要調用的函數是否存在。

我不得不承認,我沒有使用任何的這個自己,但它看起來像你對我應該能夠用它來實現你想要的...

2

加速提供BOOST_STATIC_ASSERT這一點。剛剛批准的C++標準版本將提供一個static_assert宏的內置版本。

enable_if另一方面,並​​不是真的很適合這個。它可以使用可以使用,但enable_if的主要目的是區分否則模糊的重載。