2013-03-18 96 views
1

我有一個類Foo,有兩個任意數據成員類型A和B.調用Foo :: operator()(Arg & &)將參數轉發給兩個成員並返回結果的總和。我可以看到幾種方法來實施所有必要的類型扣除。有沒有一些方法是首選,並減少編譯器的壓力?我的意思是編譯時意義上的「緊張」,如果嵌套得太深,就會觸及內部限制等等。你能概括一下它,還是對給定的編譯器來說是非常具體的?返回類型推理:什麼方法是首選?

我能做的「天真」自動decltype變種:

template <typename A, typename B> 
class Foo 
{ 
public: 
    Foo(A a, B b) : m_a(std::move(a)), m_b(std::move(b)) { } 

    template <typename Arg> 
    auto operator()(Arg && arg) -> decltype(m_a(std::forward<Arg>(arg)) + m_b(std::forward<Arg>(arg))) 
    { 
    return m_a(std::forward<Arg>(arg)) + m_b(std::forward<Arg>(arg)); 
    } 
private: 
    A m_a; 
    B m_b; 
}; 

我可以寫,只有在類型,而不是「真實」的實例,但通過的std :: declval創建者操作的輔助結構<>

template <typename A, typename B, typename Arg> 
struct Foo_Returns 
{ 
    typedef decltype(std::declval<A>()(std::declval<Arg>()) + 
        std::declval<B>()(std::declval<Arg>())) type; 
} 

template <typename A, typename B> 
class Foo 
{ 
public: 
    Foo(A a, B b) : m_a(std::move(a)), m_b(std::move(b)) { } 

    template <typename Arg> 
    typename Foo_Returns<A, B, Arg>::type 
    operator()(Arg && arg) 
    { 
    return m_a(std::forward<Arg>(arg)) + m_b(std::forward<Arg>(arg)); 
    } 
private: 
    A m_a; 
    B m_b; 
}; 

有沒有更多的可能性?

現在讓我們更難:我們有兩個特徵is_green <>並且是_blue <>。如果Arg爲綠色,則Foo的運算符()應將Arg轉發給A和B的成員函數爲綠色,並返回結果的總和,類似於Arg爲藍色。一個類型永遠不會是綠色和藍色。應該可以添加更多的類型(所以使用bool值來表示不允許藍色或綠色)。

一個變種會使用標籤調度和經銷商 - > decltype(...)儘可能:

struct green_tag { }; 
struct blue_tag { }; 
struct error_tag; 

template <typename T> 
struct category 
{ 
    typedef typename std::conditional<is_green<T>::value, 
            green_tag, 
            typename std::conditional<is_blue<T>::value, 
                   blue_tag, 
                   error_tag 
                  >::type 
            >::type type; 
} 

template <typename A, typename B> 
class Foo 
{ 
public: 
    Foo(A a, B b) : m_a(std::move(a)), m_b(std::move(b)) { } 

    template <typename Arg> 
    auto operator()(Arg && arg) -> decltype(impl(std::forward<Arg>(arg), typename category<Arg>::type())) 
    { 
    return impl(std::forward<Arg>(arg), typename category<Arg>::type()); 
    } 

private: 
    template <typename Arg> 
    auto impl(Arg && arg, green_tag) -> decltype(m_a.green(std::forward<Arg>(arg)) + m_b.green(std::forward<Arg>(arg))) 
    { 
    return m_a.green(std::forward<Arg>(arg)) + m_b.green(std::forward<Arg>(arg)); 
    } 

    template <typename Arg> 
    auto impl(Arg && arg, blue_tag) -> decltype(m_a.blue(std::forward<Arg>(arg)) + m_b.blue(std::forward<Arg>(arg))) 
    { 
    return m_a.blue(std::forward<Arg>(arg)) + m_b.blue(std::forward<Arg>(arg)); 
    } 

    A m_a; 
    B m_b; 
}; 

另一個版本可以使用的輔助結構:

template <typename A, typename B, typename Arg, typename Category = typename category<Arg>::type> 
struct Foo_Returns; 

template <typename A, typename B, typename Arg> 
struct Foo_Returns<A, B, Arg, green_tag> 
{ 
    typedef decltype(std::declval<A>().green(std::declval<Arg>()) + 
        std::declval<B>().green(std::declval<Arg>())) type; 

    type operator()(A & a, B & b, Arg && arg) const 
    { 
    return a.green(std::forward<Arg>(arg)) + b.green(std::forward<Arg>(arg)); 
    } 
}; 

template <typename A, typename B, typename Arg> 
struct Foo_Returns<A, B, Arg, blue_tag> 
{ 
    typedef decltype(std::declval<A>().blue(std::declval<Arg>()) + 
        std::declval<B>().blue(std::declval<Arg>())) type; 

    type operator()(A & a, B & b, Arg && arg) const 
    { 
    return a.blue(std::forward<Arg>(arg)) + b.blue(std::forward<Arg>(arg)); 
    } 
}; 

template <typename A, typename B> 
class Foo 
{ 
public: 
    Foo(A a, B b) : m_a(std::move(a)), m_b(std::move(b)) { } 

    template <typename Arg> 
    typename Foo_Returns<A, B, Arg>::type 
    operator()(Arg && arg) 
    { 
    return Foo_Returns<A, B, Arg>()(m_a, m_b, std::forward<Arg>(arg)); 
    } 

private: 
    A m_a; 
    B m_b; 
}; 

是任何版本的比較好?還有哪些其他方法可行?

+0

gcc-4.8用'-std = C++ 1y'和'auto'作爲返回類型。 ;) – inf 2013-03-18 17:16:51

+0

我還沒有讀你寫的所有。但是當你使用成員時,當你的成員函數是const的時候它也可以工作(成員將被視爲const)。還需要在函數之前聲明成員。就像你的代碼一樣,它不起作用。 – 2013-03-18 17:38:46

回答

0

我會避免所有的助手類/結構。每個幫助程序都要求編譯器將其存儲在某處並進行其他查找。如果沒有這些類,編譯器至少有機會優化它們,但我無法想象助手類能夠改善所展示示例的情況。

對於你的綠/藍例如,我會甚至考慮SFINAE保持代碼的短,中實例化的類/方法的數量少:

template <typename A, typename B> 
class Foo 
{ 
public: 
    Foo(A a, B b) : m_a(std::move(a)), m_b(std::move(b)) { } 

    template <typename Arg> 
    auto operator()(const Arg & arg) -> 
    typename std::enable_if< is_green<Arg>::value, 
     decltype(m_a.green(arg) + m_b.green(arg) >::type 
    { 
    return m_a.green(arg) + m_b.green(arg); 
    } 

    template <typename Arg> 
    auto operator()(const Arg & arg) -> 
    typename std::enable_if< is_blue<Arg>::value, 
     decltype(m_a.blue(arg) + m_b.blue(arg)) >::type 
    { 
    return m_a.blue(arg) + m_b.blue(arg); 
    } 

private: 
    A m_a; 
    B m_b; 
}; 

我想也想象這是更容易維護,因人而異。對於編譯時的表現,一如既往,只有一個真正的建議:測量它。

編輯:我改變的參數從Arg&&const Arg&和下降的雙重std::forward<Arg>(...),因爲這是違法的,看評論。

+1

'arg'被轉發兩次,一次爲'm_a',一次爲'm_b'。 – balki 2013-03-19 13:35:05

+0

@balki:很好的觀察,但這是OP代碼的問題。我會編輯我的答案,謝謝! – 2013-03-19 13:40:36

相關問題