7

this is my first question, I hope I do everything correct.

I try to derive a class from a boost tuple. Boost's tuples provide a get() template method to access the individual fields. Interestingly I cannot use the method from within the derived class.

The following code shows the problem:

#include <iostream>
#include <boost/tuple/tuple.hpp>
using namespace std;

template<typename A>
class Derived : public boost::tuple<A>
{
public:
    Derived() : boost::tuple<A>() {}

    A& getVal0()
    {
        return get<0>();
        // does not compile:
        //error: 'get' was not declared in this scope

        return boost::tuple<A>::get<0>();
        // does not compile
        //error: expected primary-expression before ')' token

        return boost::tuples::get<0>(*this);
        //works
    }
};  

int main() {
    Derived<int> a;

    a.get<0>() = 5;

    cout << a.get<0>() << endl; 
    cout << a.getVal0() << endl; 
    return 0;
}

I wonder why I can access the get<0>() method from the main function

a.get<0>() = 5;

but not from within the A& getVal0() method:

error: 'get' was not declared in this scope

The second return line was my try to scope the method call to the base class:

return boost::tuple<A>::get<0>();

And this generates a different error

error: expected primary-expression before ')' token

Calling the external function `boost::tuples::get<0>(*this) works. And this workaround is okay to me. But I am still wondering why I cannot use the tuple method at this point.

In the boost documentation is a notice for Visual C++

Note! The member get functions are not supported with MS Visual C++ compiler. Further, the compiler has trouble with finding the non-member get functions without an explicit namespace qualifier. Hence, all get calls should be qualified as: tuples::get(a_tuple) when writing code that should compile with MSVC++ 6.0.

But I am using GCC 4.5.2 & 4.8.1

Thanks in advance

Evil Azrael
  • 73
  • 1
  • 3
  • 6
    +1 I have to say this: The subject matter aside, your question should be held as an example to any new user that posts on this site. You have *everything* needed for a good question, including (1) the problem you think you have, (2) sample code that exhibits that problem, (3) attempts at solving the problem, (4) each and every outcome of those attempts, (5) the tools you're using including version info, and (6) research into what you think the root problem may be. For a post in-general this is awesome; for a *first* post it is simply outstanding and rarely seen with such delivery. – WhozCraig Nov 24 '13 at 20:49
  • WhozCraig it is very nice that you can give a beginner so many good words and positive advices – 4pie0 Nov 24 '13 at 20:52
  • @piotruś Not difficult when a question like this is posted. I'm dead-serious about this being exemplary. I just hope the OP knows how to mark a solution, because I'm pretty sure Dietmar has it. =P – WhozCraig Nov 24 '13 at 20:54
  • @WhozCraig. I think i know how to mark a solution as accepted. Or at least i should do. – Evil Azrael Nov 24 '13 at 21:48

2 Answers2

5

Assuming there is a get<I>() member function template in the base class, you probably want to use

this->template get<0>()

The this part is needed to make it a dependent look-up (you can use proper class qualification, too, but that's a bit of a pain and unnecessary unless you are hiding a base class name). The template part is necessary to tell the compiler that the dependent name (get) happens to be a template.

The primary reason why this (or some other qualification) and template are needed is the two phase compilation model for templates:

  • Any name which doesn't immediately depend on a template argument in some form is looked up only during phase I, i.e., in the context where the template is defined. Since the template argument is unknown and, thus, the exact layout of the base class isn't known (it may be specialized) any name in a base class is ignored. Using any qualification causing the name to depend on the template argument, e.g., using this->, moves the look-up into phase II, i.e., when the template is instantiated.
  • Once a name is dependent there arises an ambiguity if an expression involves a < character while the template is parsed in phase I, i.e., when the template arguments are not, yet, known: The < can either be the start of an explicit template argument for a member function call or it can be the less-than operator. Since explicit mention of template arguments is rare (well, at least, it was rare when these rules were made), it is assumed to be the less-than operator by default. To state that the name is actually a member function template with an explicitly specified template argument, it needs to be preceded by the keyword template (pretty similar to types needing a typename).
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • The big question was why this is happening. Thanks to the `this->template' hint I found [link](http://stackoverflow.com/questions/5533354/what-does-a-call-to-this-template-somename-do). $C.13.6 in Stroustrups The C++ Programming Language explains the problem. But i am still not sure why this is needed in the derived class. – Evil Azrael Nov 24 '13 at 22:20
  • @EvilAzrael: I added some explanation to the answer. – Dietmar Kühl Nov 25 '13 at 00:04
0

I found this question very helpful. On a large body of code which considerable inherited structure the ability to refer internally within a class to objects from base classes is very important. As code becomes more mature this facility is most likely required for templated classes. Before 2011 it was quite easy. however, it has become progressively more challenging; I realize from this question and the answer that my fixes went in the wrong direction.

The following code (a generic solution and also a version that works for the example given here), stimulated by the answer given, is the simplest way to do this that I know. Others might also find it helpful. It goes further than the answer here as it also deals with TYPES defined in base classes. The approach works fine for non-templated classes too.

#include <iostream>
#include <boost/tuple/tuple.hpp>

/// Using inheritance within a template class

template<typename U>
struct A {
    // a parameter dependent type
    typedef U ARG;

    // a member variable
    U arg = 7;

    // a member function
    void foo()
    {
        std::cout << "A::foo \n";
    }
};

template<typename T>
struct B : A<T> {
    // a shell separating A from C to represent a more complex hierarchy
};

template<typename T>
struct C : B<T> {
    // a standardized name for the local type and context for the derived class
    using This = C;

    /// C is a derived class - with distant base members and types accessible through a parameter dependent base class

    void foo1()
    {
        /// Using inherited objects within a template class does not need to know the base class or introduce a using or typedef
        /// However, one does need to tell the compiler that the names are to be interpreted in the local context
        
        /// Using a base type ARG
        typename This::ARG z = 3;

        /// Using a base function foo()
        This::foo();

        /// Using a base member arg
        This::arg = z;
        
        // this->arg and this->foo also work for the last two, auto-converting "this" to a pointer to the relevant base class
        
        std::cout  << "C::foo1 finds (without referencing any base class):"
            " the type ARG"
            ", the variable arg"
            ", and the function foo"
            ", all defined in a distant base class A<U> with U set to double. \n";
    }
};

// the example from the question
template<typename A>
class Derived : public boost::tuple<A>
{

public:
    A& getVal0()
    {
        // inform the compiler of the local context for get<>()
        return Derived::get<0>();
    }
};

int main()
{
    /// the solution to the example 
    Derived<int> a;
    a.get<0>() = 5;
    std::cout << a.get<0>() << std::endl;
    std::cout << a.getVal0() << std::endl;

    std::cout << "\n////////////////////////Generic Examples///////////////////\n\n";
    /// Using inheritance with a template class
    C<double> c;
    C<double>::ARG x = 99;
    
    std::cout 
        << "the variable c.arg: " << c.arg 
        << ", the type C<double>::ARG "<< x 
        << ", and the function c.foo() "; c.foo();
    std::cout 
        << "are all directly accessible from external code.\n\n";
    c.foo1();
}
/* Output:
5
5

////////////////////////Generic Examples///////////////////

the variable c.arg: 7, the type C<double>::ARG 99, and the function c.foo() A::foo
are all directly accessible from external code.

A::foo
C::foo1 finds (without referencing any base class): the type ARG, the variable arg, 
and the function foo, all defined in a distant base class A<U> with U set to double.
*/

The approach is simple and effective. Just as typename is needed to say when a type is a type, it seems that modern C++ enforces that you tell it (at least in templates) that a name is defined in the hierarchy above the derived class by prefixing the name with the derived class name. This is all that is needed and can obviously be done for non-templated classes without error.

I used to develop templated classes by developing an instance first and then adding the template<class ..> when it was working. This stopped working because of this challenge of accessing members of base classes; it is no longer an issue. I just use the This:: syntax in the initial class definitions.

This code works in C++2020 as well as 2011.

tjl
  • 131
  • 1
  • 5