2016-07-14 43 views
3

我目前正在實現一個數據集輔助類模板,該模板存儲浮點值(Scalar),動態調整Eigen::Matrix不同的值類型(Element)額外存儲對此輸入向量的引用。現在我想部分地專門化矢量值類型中的構造函數,並保留要顯式實例化的標量類型中的模板。C++,2參數類模板的部分特化:無法將函數定義與現有聲明匹配

不幸的是我得到「無法定義的功能匹配到現有的宣言」於2010年VS的代碼很簡單,只要:

template <class Scalar, class Element> struct DataSet 
{ 
    DataSet(std::vector<Element> const & source); 

    // several generic member functions here ... 

    Eigen::Matrix<Scalar, ... > data; 
    std::vector<Element> const & source; 
}; 

template<class Scalar> 
DataSet<Scalar, SomeClass>::DataSet(std::vector<SomeClass> const & input) 
{ 
    // special impl for Element==SomeClass ... 
} 

SomeClass的應自動對其進行編譯的時候想通了這樣做的權利,但我嘗試了所有有意義的組合,但仍然得到:

*.cpp(79) C2244 : unable to match function definition to an existing declaration 
see declaration of 'DataSet<Scalar, Element>::DataSet' 

我是不是能夠通過搜索互聯網還沒有找到一個匹配的例子。提前致謝!

編輯

爲了使它更具體,在我的現實世界的情況下,我希望能夠定義多個部分特例與不同類型的構造Element如:

template<Scalar> 
DataSet<Scalar, FirstClass>::DataSet(std::vector<FirstClass> const & first) 
: data() 
, source(first) 
{ 
    // special impl here ... 
} 

template<Scalar> 
DataSet<Scalar, std::shared_ptr<SecondClass> >::DataSet(std::vector<std::shared_ptr<SecondClass> > const & second) 
: data() 
, source(second) 
{ 
    // special impl here ... 
} 

不希望將類完全重新聲明/專門化爲某個類型名稱。那麼根本沒有什麼用處可以成爲一個模板。我想要解決方案,否則可能會有其他策略來解決我的問題。

FIN

因爲它看起來像不是可以通過僅對專業構造(這是某種相關類的隱含專業化)分享類模板和構造的類型Element我刪除了完全從類模板中引用source,並將所需信息複製到通用容器中,並通過重載實現構造函數。

回答

3

根據§14.7.3.16:

在一個明確的專業化聲明一個類模板的成員或出現在命名空間內的成員模板時,成員模板和它的一些外圍類模板可能除非聲明不明確地專門化一個類成員模板,如果它的封閉類模板也沒有明確專用的話。

不過,你可以使用std :: enable_if到局部專門的構造器:

template <class Scalar, class Element> struct DataSet 
{ 
    template <class T> 
    DataSet(std::vector<T> const & input, std::enable_if_t<!std::is_same<T, SomeClass>{}> * = nullptr) { 
     std::cout << "Element\n"; 
    } 
    template <class T> 
    DataSet(std::vector<T> const & input, std::enable_if_t<std::is_same<T, SomeClass>{}> * = nullptr) { 
     std::cout << "SomeClass\n"; 
    } 
}; 

但這種方式是有限制:

  • 所有的條件必須是獨家
  • 你將不得不修改你想要處理的每個新類的類的代碼。

相反,我建議你使用模板助手結構:

DataSet(std::vector<Element> const & input) { 
    Helper<Element>::do_it(input); 
} 

,你可以專門爲你想要的:

template <class Element> 
struct Helper { 
    static void do_it(std::vector<Element> const & input) { 
     std::cout << "General form with Element\n"; 
    } 
}; 


template<> 
struct Helper<SomeClass> { 
    static void do_it(std::vector<SomeClass> const & input) { 
     std::cout << "SomeClass\n"; 
    } 
}; 

template<> 
struct Helper<SomeOtherClass> { 
    static void do_it(std::vector<SomeOtherClass> const & input) { 
     std::cout << "SomeOtherClass\n"; 
    } 
}; 

... 
+1

感謝您的幫助,但這不會直接引用源數據,並增加了代碼行,與我最初希望儘可能簡單的想法不同。我現在刪除了對源數據的引用,並將這些信息抽象爲一些通用容器。構造函數現在可以實現爲沒有'Element'的重載。但是我仍然很好奇這個問題出在哪裏,因爲總的來說,開始的方式應該是從句法的角度去做的方式。 – daku

+0

@daku假設專門的模板會從主要的「基礎」中「繼承」成員,這很直觀,但事實並非如此。這是因爲模板參數可能意味着某些成員在某些專業領域完全不適用,從而導致隱含的「繼承」問題。如果是這樣,它必須被決定要求完全重新宣告,並且不應該判斷值得添加大量的神祕語法來指定成員是否應該被「繼承」......當你可以簡單地使用_actual_繼承來確保它們是。但是,我會看看我能否找到正確的記錄理由 –

+0

@underscore_d:這是因爲專業化不是繼承;-)專門化類模板會創建一個完整的新類。我的目標是專門化構造函數,這顯然是不可能的,因爲'Element'是類模板的模板參數。如果我使構造函數成爲模板函數,然後可能不可能在類和函數模板之間共享參數'typename Element'(對於ctor,或者它以某種方式?),應該很容易做到這一點。 – daku

3

定義你的構造函數時,你沒有明確提供它的類的兩個模板參數。這將需要進行如下修改:

template<typename T_Scalar, typename T_Element> 
DataSet<T_Scalar, T_Element>     // template args for type 
::DataSet(std::vector<T_Element> const &input) // but none for constructor 
{ 
    // stuff 
} 

切向有關:不同的方法,template arguments for classes cannot be deduced from constructor calls。那就是:until C++17 comes around!嗚!

您面臨的下一個絆腳石是模板專業化不會從其主模板「繼承」成員。假設他們願意,這有點直觀,但事實並非如此。直到我找到一個官方的理由,我假設這是因爲模板參數可能會使某些成員完全不適用於專業化,呈現隱含的「繼承」有問題。如果是這樣,它會被決定要求完全重新聲明/不判斷值得添加奧術語法來指定哪些主要「基本」成員是「繼承」......當你可以簡單地使用實際繼承來確保它們是。

無論如何,這意味着要獲得部分專業化,您需要聲明整個事情 - 在這種情況下,該類及其構造函數 - 纔可以專門化該構造函數的定義。你還沒有提前聲明,所以編譯器正確地抱怨說它看不到聲明。

// Define specialised class 
template<typename T_Scalar> 
class DataSet<T_Scalar, SomeClass> 
{ 
public: 
    // Declare its ctor 
    DataSet(std::vector<SomeClass> const &); 
} 

// Implement its ctor 
template<typename T_Scalar> 
DataSet<T_Scalar, SomeClass> // complete template args 
::DataSet(std::vector<SomeClass> const &input) 
{ 
    // stuff 
} 

See my working example of an equivalent template class, showing general vs. specialised instantiations.

要添加到你原來的混亂,這是公平的! - 注意,如果模板類本身包含模板函數,則線外定義可能會變得非常複雜,因爲那樣你就需要使用 template子句。

template<typename TA, typename TB> 
class Widget { 
    template<typename TC> 
    void accept_gadget(TC &&gadget); 
}; 

template<typename TA, typename TB> 
template<typename TC> 
Widget<TA, TB> 
::accept_gadget(TC &&gadget) 
{ 
    /* ... */ 
} 

東西,這將幫助很多在許多情況下,特別是包括了這樣的行模板定義,如果是被the proposal to allow namespace class在未來版本中接受的。非常傷心,這並沒有使它成爲C++ 17 ...而且很奇怪它在第一位失蹤了!

+0

好吧,我是莫名其妙地在恐懼中,我期待有些不支持c + +模板(因爲我沒有找到匹配的例子),但我應該能夠讓它工作,不是嗎?我很可能需要稍後對輸入向量的引用,這就是爲什麼'Element'應該保持爲類的模板參數,而不是構造函數......任何建議? – daku

+0

@daku是的,它是一個模板參數,所以把它作爲一個包含它!看到我的編輯 –

+0

是不是通過不同於一般形式的'Element' ='SomeClass'獲得部分專業化的目的? –

相關問題