简体   繁体   中英

Why does this compile, Template deduction should fail?

I'm attempting to write a serialiser. The following code compiles:

#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);
}

I'd have thought that this line:

stream.Add("xr",m_rx);

would have failed because of the two Add functions, one specifically checks that the type isn't a class, the other checks that it's not a reference. m_rx is a reference type, so it should fail?

EDIT I understand now that the type is actually a value and not a reference. I need to be able to identify references so that I can keep track of them (I only want to serialise the data once, and reference it).

According to expr#5

If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression. [ Note: Before the lifetime of the reference has started or after it has ended, the behavior is undefined (see [basic.life]). — end note ]

I think the argument type A will never be a reference type when performing template argument deduction. A simple test could be

#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;
}

One walk-around I can think of is explicitly specify template argument using decltype

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM