2012-05-12 38 views
3

我正在爲C++ 11「LINQ to Objects」庫。 我想這樣做水木清華這樣的:通過函子參數計數類型C++重載

// filtering elements by their value 
arr.where([](double d){ return d < 0; }) 

// filtering elements by their value and position 
arr.where([](double d, int i){ return i%2==0; }) 

我倒想寫arr.where_i(...) - 它是醜陋的。 所以我需要通過拉姆達型函數/方法重載...

這是我的解決方案:

template<typename F> 
auto my_magic_func(F f) -> decltype(f(1)) 
{ 
    return f(1); 
} 

template<typename F> 
auto my_magic_func(F f, void * fake = NULL) -> decltype(f(2,3)) 
{ 
    return f(2,3); 
} 

int main() 
{ 
    auto x1 = my_magic_func([](int a){ return a+100; }); 
    auto x2 = my_magic_func([](int a, int b){ return a*b; }); 
    // x1 == 1+100 
    // x2 == 2*3 
} 

是否SFINAE解決方案嗎? 你能告訴我什麼?

+0

該解決方案的工作原理,但我需要知道類型的參數寫my_magic_func。它不舒服。 – k06a

+0

你可能想看看boost :: range。 –

+0

我在圖書館工作的主要目標是複製C#LINQ風格...如果你有興趣,你可以在這裏得到它:http://code.google.com/p/boolinq/ – k06a

回答

3

也許一些可變參數:

#include <utility> 

template <typename F, typename ...Args> 
decltype(f(std::declval<Args>()...) my_magic_func(F f, Args &&... args) 
{ 
    return f(std::forward<Args>(args)...); 
} 

編輯:您還可以使用typename std::result_of<F(Args...)>::type返回類型,它做同樣的事情。

+1

我很抱歉,但MSVC++ 2010不支持可變模板 – k06a

+0

您不能使用'std :: result_of'。它指定的方式並不能保證使用它會產生正確的SFINAE,並且根據我的經驗,在某些情況下你會遇到嚴重的錯誤(這意味着它似乎在其餘時間都有效)。 –

+0

-1這完全忽略了問題的重點。 'my_magic_func'應該是一元的,不需要參數包;它應該檢測它給出的仿函數的arity,並相應地用不同的參數調用它。 – ildjarn

2

您當然希望在您的解決方案中使用SFINAE。一般來說,結果會是這樣:

template< 
    typename Functor 
    , typename std::enable_if< 
     special_test<Functor>::value 
     , int 
    >::type = 0 
> 
return_type 
my_magic_func(Functor f); 

template< 
    typename Functor 
    , typename std::enable_if< 
     !special_test<Functor>::value 
     , int 
    >::type = 0 
> 
return_type 
my_magic_func(Functor f); 

這樣,只有一個過載會活躍在任何一個時間 - 這一切仍然正在認真起草該special_test有我們想要的行爲。這是一種謹慎的平衡行爲,因爲您不希望測試過於具體;否則我們失去了普遍性。編寫泛型代碼時非常遺憾。您沒有提供太多的信息(例如,您是否嚴格支持lambda?monomorphic functors?多態函數?),但現在我假設您可以訪問value_type別名,這將與您的示例中的double相對應。

因此,下面是一個示例條件,它將檢查給定類型是Callable(這是一個標準概念),其簽名爲bool(value_type);也就是說,它是某種形式的謂語:

template<typename Functor, typename ValueType> 
struct is_unary_predicate { 
    typedef char (&accepted)[1]; 
    typedef char (&refused)[2]; 

    void consume(bool); 

    template< 
     typename X 
     , typename Y 
     , typename = decltype(consume(std::declval<X>()(std::declval<Y>()))) 
    > 
    accepted 
    test(X&&, Y&&); 

    refused test(...); 

    static constexpr bool value = 
     sizeof test(std::declval<Functor>(), std::declval<ValueType>()) 
     == sizeof(accepted); 
}; 

我個人有一個is_callable<F, Signature>特質,這樣我就只需要編寫類似template<typename Functor, typename ValueType> using is_unary_predicate = is_callable<Functor, bool(ValueType)>;(類似我能有一個is_binary_predicate別名,而不是讓的my_magic_func第二超載一竅不通)。也許你想用SFINAE的未來使用類似的特點(雖然寫沒有可變模板可能會有點痛苦)。

+0

不應該「接受」和「拒絕」是不同的大小? – ildjarn

+0

@ildjarn謝謝,修正。 –