为什么这样编译,模板扣除应该失败?

问题描述:

我正在尝试编写一个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

根据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