How to use trailing return type with a templated class member

546 views Asked by At

I'm trying to implement the following class:

template <typename Container>
class reverse_adaptor
{
public: // Construction
    reverse_adaptor(Container &container) :
        m_container(container)
    {}

public: // STL container static polymorphism
    auto begin() const -> decltype(m_container.rbegin())
    {
        return m_container.rbegin();
    }

    auto end() const -> decltype(m_container.rend())
    {
        return m_container.rend();
    }

private: // Members
    Container &m_container;
};

The reason I'm using the trailing return type is because I don't know if m_container will be const or not, so I let the compiler work it out for me. However, I'm getting the following compiler errors:

/Users/mark/Blah/Stdx.h:77:40: No member named 'm_container' in 'stdx::reverse_adaptor > >'

I thought it might be related to the multi-stage pass of templated types, so changed it to read decltype(this->m_container.rbegin()), but that didn't work either.

How can I get this to work?

Example - http://ideone.com/ekVYlH

2

There are 2 answers

1
gx_ On BEST ANSWER

The trailing-return-type of a function is part of its “signature” (declaration), not of its “body” (definition), and as such, it only sees names that were declared before.

At the point where you declare your begin member function, m_container hasn't been declared yet. (Note that the issue is not specific to template classes.)

  • You could move the declaration of m_container up in the class definition (but it forces you to put private members before public interface, which is contrary to common practice...).

  • You can work-around with declval: replace m_container with std::declval<Container&>() inside the decltype: http://ideone.com/aQ8Apa

(As said in the comments, in C++14 you'll be able to drop the trailing return type and just use decltype(auto) as the “normal” return type.)


Addendum: As for why you can use not-yet-declared members inside the in-class body of a member function but not in the trailing return type, it's because the following:

class Foo {
public:
    auto data() const -> decltype(m_data)
    {
        return m_data;
    }
private:
    SomeType m_data;
};

will be [disclaimer: informal wording!] kind of “rewritten by the compiler” into something equivalent to this:

class Foo {
public:
    inline auto data() const -> decltype(m_data); // declaration only
private:
    SomeType m_data;
};

// at this point, all members have been declared

inline auto Foo::data() const -> decltype(m_data) // definition
{
    return m_data;
}

where a name cannot be used before its declaration (which the first decltype(m_data) violates).

0
K-ballo On

The name m_container has not been introduced at the point the functions are declared. Switch the order of declarations, and the code will work.

See working version here.