2016-09-21 69 views
1

我正在嘗試編寫一個serialiser。下面的代碼編譯:爲什麼這樣編譯,模板扣除應該失敗?

#include <string> 
#include <fstream> 
#include <type_traits> 
#include <map> 
#include <iostream> 

class SpaceStream 
{ 
public: 

    SpaceStream(const std::string& filename) 
    : 
     m_file(filename) 
    { 
    } 

    template<typename T> 
    typename std::enable_if<std::is_class<T>::value>::type 
    Add(const std::string& key, const T& t) 
    { 
     m_file << key; 
     m_file << ":{"; 
     t.Serialise(*this); 
     m_file << "},"; 
    } 

    template<typename T> 
    typename std::enable_if<!std::is_class<T>::value && !std::is_pointer<T>::value && !std::is_reference<T>::value>::type 
    Add(const std::string& key, const T t) 
    { 
     m_file << key; 
     m_file << ':'; 
     m_file << t; 
     m_file << ','; 
    } 

private: 
    std::ofstream m_file; 
    std::map<std::string,std::string> m_pointerObj; 
}; 


class ISerialise 
{ 
public: 
    virtual void Serialise(SpaceStream& stream) const = 0; 
}; 

class Test1 : public ISerialise 
{ 
public: 
    int m_x; 
    int& m_rx; 

    Test1(int& x) 
    : 
     m_x(x), m_rx(x) 
    { 
    } 

    virtual void Serialise(SpaceStream& stream) const 
    { 
     stream.Add("x",m_x); 
     stream.Add("xr",m_rx); 
    } 
}; 

int main() 
{ 
    int j = 13; 
    Test1 test(j); 
    j = 23; 

    SpaceStream ss("somefile.ss"); 
    ss.Add("testobj",test); 
} 

我還以爲這條線:

stream.Add("xr",m_rx); 

會因爲兩個Add功能失效,一個專門檢查該類型不是一類,另一方檢查它不是參考。 m_rx是一個引用類型,所以它應該失敗?

編輯 我現在明白,類型實際上是一個值,而不是一個參考。我需要能夠識別引用,以便我可以跟蹤它們(我只想將數據序列化一次,並引用它)。

+0

指定的模板參數「*我需要能夠識別引用,這樣我可以跟蹤他們*「你是什麼意思? –

+1

這與真空沒有什麼不同,它有一個函數'void f(int)'並用一個引用'int'的值來調用它。 –

+0

這與模板無關,問題依然存在。當作爲函數參數傳遞時,變量本身始終是左值引用(無模板)。然後,根據你的函數簽名,你可以創建一個對象(成爲一個副本)(按值傳遞),或者創建一個綁定到傳入引用的左值引用。 (通過參考)。但是在調用表達式中沒有辦法指定想要傳遞參數的方式。你不能同時使用'void foo(int);'和'void foo(int&)'並調用'int i = 0; foo(i)'因爲它是不明確的 – DimG

回答

2

根據expr#5

如果表達式最初具有類型爲「參照T」([dcl.ref],[dcl.init.ref]),類型至T任何之前調整更深入的分析。表達式指定由引用表示的對象或函數,表達式是左值或xvalue,具體取決於表達式。 [注意:在參考的生命週期開始之前或結束之後,行爲是不確定的(參見[basic.life])。 - 尾註]

我認爲參數類型A在執行模板參數演繹時絕不會是引用類型。一個簡單的測試可能是

#include <type_traits>  

template <class T> void f(T) { static_assert(std::is_same<T, int &>::value, "ERROR"); } 
template <class T> void ff(T) { static_assert(std::is_same<T, int>::value, "ERROR"); } 

int main(int argc, const char **argv) { 
    int i; 
    int &r = i; 
    f(r); // static assert failed 
    ff(r); // static assert success 
    return 0; 
} 

一個步行約我能想到的是明確使用decltype

f<decltype(r)>(r); // static assert success now 
+2

*「我認爲參數類型A永遠不會是執行模板參數推演時的參考類型」*,除了轉發參考類型 –

+0

@PiotrSkotnicki我不完全理解您的觀點。如果你的意思是'std :: forward',那麼根本就沒有模板參數的推論,因爲在使用它的時候我們需要明確地提供模板參數。至於轉發參考類型扣除,'A'部分仍然不能成爲引用類型。 – Carousel