2011-09-11 16 views
5

我想指定從A<T>::BA<U>::B的轉換。內部類中的通用拷貝構造函數

template<typename T> 
struct A 
{ 
    struct B 
    { 
     B() {} 

     template<typename U> 
     B(const typename A<U>::B& rhs) {} 
    }; 
}; 

int main() 
{ 
    A<int>::B x; 
    A<double>::B y = x; 
} 

我認爲這將做到這一點,但我得到的編譯器錯誤:

conversion from ‘A<int>::B’ to non-scalar type ‘A<double>::B’ requested"

爲什麼不是我的代碼正確的,什麼是達到預期的轉換的正確方法?

+0

澄清,什麼是所需的轉換?因爲您不是將int轉換爲double,而是將A :: B轉換爲A :: B,這是另一個類的類。 – SSight3

+0

@ SSight3:讓我猜猜:Robert試圖用外部refcounts和多態分配來重新實現智能指針... – sehe

+0

轉換是從A :: B到A :: B對於任何T,U。 int和double只是我用過的例子。不,這不是我正在做的事。 –

回答

3

稍作修改來源,以證明這是關於限制類型推演系統:

template<typename T> 
struct A 
{ 
    struct B 
    { 
     B() {} 

     template<typename U> 
      B(const typename A<U>::B& rhs) {} 

     template<typename U> 
      B& operator=(const typename A<U>::B& rhs) {} 

     template<typename U> 
      B& something(const typename A<U>::B& rhs) {} 
    }; 
}; 

int main() 
{ 
    A<int>::B x; 

    A<double>::B y(x);  // fails to deduce 
    A<double>::B y = x; // fails to deduce 
    A<double>::B y; y = x; // fails to deduce 

    x.something(y);  // fails to deduce 
    x.something<double>(y);// NO PROBLEM 
} 

你看,當我們幫助編譯器一點點,沒有更多的問題。還要注意實際的編譯器錯誤(GCC)顯示了它的困惑:

test.cpp|24 col 15| note: candidate is: 
test.cpp|15 col 44| note: template<class U> A<T>::B& A<T>::B::something(const typename A<U>::B&) 
[with U = U, T = int, A<T>::B = A<int>::B, typename A<U>::B = A<T>::B] 

注意的部分,其中U = U(即未解決)

4

模板不能是拷貝構造函數。 §12.8/ 2,腳註:

Because a template constructor is never a copy constructor, the presence of such a template does not suppress the implicit declaration of a copy constructor. Template constructors participate in overload resolution with other constructors, including copy constructors, and a template constructor may be used to copy an object if it provides a better match than other constructors.

由於模板的說法是const &,其特徵將是完全一樣的複製情況下,隱式聲明的函數,所以它永遠不會被用作拷貝構造函數。

這可能是好的,因爲在此示例中,您正在使用它作爲轉換構造函數。但是,::之前的模板參數是未推導的上下文,因此編譯器無法插入A<int>::B並解析int。由於專用模板的各種方式,編譯器無法確定哪些A(如果有)符合要求。您可以在A<float>之內添加typedef A<int>::B B;,然後intfloat將符合U的條件。

您可以通過使用SFINAE並向類中添加成員類型來解決此問題,以幫助導航層次結構。這裏是一個demo.

#include <typeinfo> 
#include <iostream> 

template<typename T> 
struct A 
{ 
    typedef T type; 

    struct B 
    { 
     B() {} 

     template<typename U> 
     B(const U& rhs, typename U::nest_A_parent * = NULL) { 
      std::cout << "copied from type " 
      << typeid(typename U::nest_A_parent::type).name() << '\n'; 
     } 

    private: 
     typedef A nest_A_parent; 
     template< typename U > 
     friend struct B; 
    }; 
}; 

int main() 
{ 
    A<int>::B x; 
    A<double>::B y(x); 
} 
2

如何做一個轉換構造函數(如Potatoswatter指出,它不能定義一個拷貝構造函數),只有匹配的嵌套式B,任何A<T>::B

namespace detail { 

// private type to identify all A<T>::B 
struct B {}; 

} // detail 

// trait to identify all A<T>::B 
// a template alias could also be used here 
template<typename T> 
struct is_b: std::is_base_of<detail::B, T> {}; 

template<typename T> 
struct A 
{ 
    struct B: detail::B { 
     B() {} 

     template<typename U> 
     B(U&& u) 
     { 
      static_assert(is_b<typename std::decay<U>::type>::value 
          , "Conversion only allowed from A<T>::B"); 
     } 
    }; 
}; 

該技術的優點是不使用SFINAE,因此無效的轉換嘗試將通過static_assert進行報告,而不是以無提示的方式失敗。另一方面,如果至少有一個其他模板轉換構造函數,則需要SFINAE。

這是假定A<T>::B將不同於A<U>::B(對於不同的TU)。如果嵌套類型相同(例如您的簡化示例代碼),您最好通過在其他位置定義B並在A中使用typedef some_private_namespace::B B;來實現。