2014-02-14 57 views
1

我玩了一點轉發,並得到以下示例,它工作正常。rvalue和左值引用的模板專門化

void Func2(int& a, int& b) { cout << "V1" << endl; } 
void Func2(int&& a, int& b) { cout << "V2" << endl; } 
void Func2(int& a, int&& b) { cout << "V3" << endl; } 
void Func2(int&& a, int&& b) { cout << "V4" << endl; } 

    template < typename T, typename U> 
void Func(T&& t, U&& u) 
{ 
    X::Func3(std::forward<T>(t), std::forward<U>(u)); 
    Func2(std::forward<T>(t), std::forward<U>(u)); 
} 

int main() 
{ 
    int a, b; 
    Func(a, b); 
    Func(1, b); 
    Func(a, 2); 
    Func(1, 2); 

    return 0; 
} 

但我想也有Func2函數模板與任何類型的,或者無法與專門方法的類來代替類型int。下面的代碼片段不會編譯:

class X 
{ 
    public: 
     template < typename T, typename U> 
      static void Func3(T& t, U& u) { cout << "X1" << endl; } 

     template < typename T, typename U> 
      static void Func3(T&& t, U& u) { cout << "X2" << endl; } 

     template < typename T, typename U> 
      static void Func3(T& t, U&& u) { cout << "X3" << endl; } 

     template < typename T, typename U> 
      static void Func3(T&& t, U&& u) { cout << "X4" << endl; } 
}; 

結果:

main.cpp: In instantiation of 'void Func(T&&, U&&) [with T = int&; U = int&]': 
main.cpp:36:18: required from here 
main.cpp:29:57: error: call of overloaded 'Func3(int&, int&)' is ambiguous 
     X::Func3(std::forward<T>(t), std::forward<U>(u)); 
                 ^
main.cpp:29:57: note: candidates are: 
main.cpp:9:29: note: static void X::Func3(T&, U&) [with T = int; U = int] 
       static void Func3(T& t, U& u) { cout << "X1" << endl; } 
          ^
main.cpp:12:29: note: static void X::Func3(T&&, U&) [with T = int&; U = int] 
       static void Func3(T&& t, U& u) { cout << "X2" << endl; } 
          ^
main.cpp:15:29: note: static void X::Func3(T&, U&&) [with T = int; U = int&] 
       static void Func3(T& t, U&& u) { cout << "X3" << endl; } 
          ^
main.cpp:18:29: note: static void X::Func3(T&&, U&&) [with T = int&; U = int&] 
       static void Func3(T&& t, U&& u) { cout << "X4" << endl; } 
          ^
+0

請添加編譯器錯誤消息。 – BoBTFish

回答

2

正如其他答案所說,這些調用是不明確的,因爲通用引用T&&, U&&匹配左值和右值引用。您可以使用std::enable_if手動刪除歧義,例如

template <bool C> 
using only_if = typename std::enable_if <C>::type; 

template <typename T> 
using is_lref = std::is_lvalue_reference <T>; 

struct X 
{ 
    template <typename T, typename U> 
    static void 
    Func3(T& t, U& u) { cout << "X1" << endl; } 

    template <typename T, typename U> 
    static only_if <!is_lref <T>()> 
    Func3(T&& t, U& u) { cout << "X2" << endl; } 

    template <typename T, typename U> 
    static only_if <!is_lref <U>()> 
    Func3(T& t, U&& u) { cout << "X3" << endl; } 

    template <typename T, typename U> 
    static only_if <!(is_lref <T>() || is_lref <U>())> 
    Func3(T&& t, U&& u) { cout << "X4" << endl; } 
}; 

另請參閱live example。這樣你明確地說T&&不應該匹配一個左值引用。

這種方法很難推廣到更多的輸入參數。在這種情況下,請考慮一次只處理一個參數,剩下的參數作爲右值參考。因此,您只需要兩次重載和遞歸調用,但確切的形式取決於您想要執行的操作。

+0

'only_if'通常被命名爲'enable_if_t'(如果我們保留默認參數)。 – Jarod42

+0

'!(is_lref ()|| is_lref ())''可以重寫'!is_lref ()&&!is_lref ()'避免否定括號。 – Jarod42

+0

@ Jarod42謝謝,我知道'enable_if_t'應該可以工作,我只是嘗試過一次而沒有工作,所以我只是匆忙地做出自己的定義。 – iavr

1

這是因爲牛逼& &是一個模板函數非常特殊的,不是右值引用。它被命名爲通用引用並綁定到參數的種類。因此,幾個Func3的候選人會得到相同的綁定,並以不明確的呼叫結束。

下面的代碼顯示「true false true」並顯示通用引用的行爲。

#include <iostream> 
#include <type_traits> 

template <typename T> 
bool foo(T&& v) { 
    return std::is_rvalue_reference<decltype(v)>::value; 
} 

int main() { 
    std::cout << std::boolalpha; 
    std::cout << foo(1) << std::endl; 
    int a{}; 
    std::cout << foo(a) << std::endl; 
    std::cout << foo(std::move(a)) << std::endl; 
} 

重載常量牛逼&和T & &不僅是一個很好的解決方案爲T & &會比常量牛逼&更好的匹配。這與非模板函數不同。

#include <iostream> 
#include <type_traits> 

template <typename T> 
bool foo(T&& v) { 
    return false; 
} 

template <typename T> 
bool foo(T const & v) { 
    return true; 
} 

bool bar(int&& v) { 
    return false; 
} 

bool bar(int const & v) { 
    return true; 
} 

int main() { 
    std::cout << std::boolalpha; 
    int a{}; 
    int const b {}; 
    std::cout << foo(a) << std::endl; // false 
    std::cout << foo(b) << std::endl; // true 

    std::cout << bar(a) << std::endl; // true 
    std::cout << bar(b) << std::endl; // true 
} 
3

以下可能會有所幫助:

template <typename T, typename U> 
class X 
{ 
public: 
    static void Func3(T& t, U& u) { cout << "X1" << endl; } 
    static void Func3(T&& t, U& u) { std::cout << "X2" << std::endl; } 
    static void Func3(T& t, U&& u) { cout << "X3" << endl; } 
    static void Func3(T&& t, U&& u) { cout << "X4" << endl; } 
}; 

template <typename T, typename U> 
void Func(T&& t, U&& u) 
{ 
    X<typename std::decay<T>::type, typename std::decay<U>::type>::Func3(std::forward<T>(t), std::forward<U>(u)); 
    Func2(std::forward<T>(t), std::forward<U>(u)); 
} 

所以FUNC3使用真正的R值的參考,而不是普遍的參考。

+0

模板類中的靜態非模板函數是一個不錯的技巧,比在SO上提出的通常的enable_if更好。但是,我認爲在實際情況下,我們想將Func3代碼因素化,這是模板的最初目的。 – galop1n

+0

如果讓整個設計允許'X'爲模板,這確實很不錯。然而,爲什麼'std :: decay'而不僅僅是'std :: remove_reference'?在這種情況下,輸入'const T&'會起作用嗎? – iavr

+0

@iavr:相當於'Func2'。 'Func2'與'const int&'不匹配。使用'std :: remove_reference',我會匹配它。 – Jarod42