4

I have a class MyVariable that holds an object and does some extra work when this object has to be modified. Now I want to specialize this to MyContainer for container objects that perform this extra work only when the container itself is modified (e.g. via push_back()) but not its elements.

My code looks like this:

template<typename T>
class MyVariable
{
public:
    //read-only access if fine
    const T* operator->() const {return(&this->_element);}
    const T& operator*()  const {return( this->_element);}

    //write acces via this function
    T& nonconst()
    {
        //...here is some more work intended...
        return(this->_element);
    }
protected:
    T _element;
};


template<typename T>
class MyContainer: public MyVariable<T>
{
public:
    template<typename Arg>
    auto nonconst_at(Arg&& arg) -> decltype(MyVariable<T>::_element.at(arg))
    {
        //here I want to avoid the work from MyVariable<T>::nonconst()
        return(this->_element.at(arg));   
    }
};


#include <vector>
int main()
{
    MyContainer<std::vector<float>> container;
    container.nonconst()={1,3,5,7};
    container.nonconst_at(1)=65;
}

However, with GCC4.7.2 I get an error that I cannot access _element because it is protected.

test1.cpp: In substitution of 'template<class Arg> decltype (MyVariable<T>::_element.at(arg)) MyContainer::nonconst_at(Arg&&) [with Arg = Arg; T = std::vector<float>] [with Arg = int]':
test1.cpp:39:25:   required from here
test1.cpp:17:4: error: 'std::vector<float> MyVariable<std::vector<float> >::_element' is protected
test1.cpp:26:7: error: within this context
test1.cpp: In member function 'decltype (MyVariable<T>::_element.at(arg)) MyContainer<T>::nonconst_at(Arg&&) [with Arg = int; T = std::vector<float>; decltype (MyVariable<T>::_element.at(arg)) = float&]':
test1.cpp:17:4: error: 'std::vector<float> MyVariable<std::vector<float> >::_element' is protected
test1.cpp:39:25: error: within this context
test1.cpp: In instantiation of 'decltype (MyVariable<T>::_element.at(arg)) MyContainer<T>::nonconst_at(Arg&&) [with Arg = int; T = std::vector<float>; decltype (MyVariable<T>::_element.at(arg)) = float&]':
test1.cpp:39:25:   required from here
test1.cpp:17:4: error: 'std::vector<float> MyVariable<std::vector<float> >::_element' is protected
test1.cpp:26:7: error: within this context

What's going on here?

4
  • 2
    It looks like a compiler bug. In ideone.com/KMi5OK your code compiles. Commented Dec 2, 2015 at 15:25
  • Also runs fine on gcc5.2.0 but gives error on visual studio 2015, so indeed a compiler problem!
    – vishal
    Commented Dec 2, 2015 at 15:32
  • CLion : compiles fine
    – Naman
    Commented Dec 2, 2015 at 15:36
  • @nullpointer: the IDE is irrelevant. Commented Dec 2, 2015 at 15:40

1 Answer 1

3

The problem does seem to be specific to the use of decltype() - if I explicitly declare nonconst_at() to return T::value_type&, thus:

template<typename Arg>
typename T::value_type& nonconst_at(Arg&& arg)

then GCC 4.8.2 compiles it with no warnings or errors. That's fine for standard containers, but obviously doesn't help every situation.

Actually calling this->_element.at(arg) isn't a problem: I can omit the trailing return type and have the compiler infer it:

template<typename Arg>
auto& nonconst_at(Arg&& arg)
{
    //here I want to avoid the work from MyVariable<T>::nonconst()
    return this->_element.at(std::forward<Arg>(arg));
}

with just a warning (which disappears with -std=c++1y) and no errors. I still need the this->, because _element is a member of a dependent base class (thanks, Simple).

EDIT - additional workaround:

As you're only interested in the type of the return value of T::at(), you can use the decltype of calling it with any T you like, even a null pointer:

template<typename Arg>
auto nonconst_at(Arg&& arg) -> decltype(((T*)nullptr)->at(arg))
{
    //here I want to avoid the work from MyVariable<T>::nonconst()
    return this->_element.at(std::forward<Arg>(arg));
}

It's ugly, but it does seem to work.

1
  • 1
    You have to write this->_element because _element is a member of a dependant base class.
    – Simple
    Commented Dec 2, 2015 at 15:55

Not the answer you're looking for? Browse other questions tagged or ask your own question.