2

This code works:

struct Defs
{
    static const int a = 1;
    int b{};
    void g() {}
};

struct Bob : Defs
{
    void f()
    {
        cout << a << "\n";
        cout << b << "\n";
        g();
    }
};

int main()
{
    Bob b;
    b.f();
}

But this code doesn't:

struct Defs
{
    static const int a = 1;
    int b{};
    void g() {}
};

template<class D>
struct Bob : D
{
    void f()
    {
        cout << a << "\n";
        cout << b << "\n";
        g();
    }
};

int main()
{
    Bob<Defs> b;
    b.f();
}

Errors:

prog.cpp: In member function 'void Bob<D>::f()':
prog.cpp:16:11: error: 'a' was not declared in this scope
   cout << a << "\n";
           ^
prog.cpp:17:11: error: 'b' was not declared in this scope
   cout << b << "\n";
           ^
prog.cpp:18:5: error: there are no arguments to 'g' that depend on a template parameter, so a declaration of 'g' must be available [-fpermissive]
   g();
     ^
prog.cpp:18:5: note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)

But if I do the following, it works:

template<class D>
struct Bob : D
{
    void f()
    {
        cout << D::a << "\n";
        cout << D::b << "\n";
        D::g();
    }
};

Is it possible to get a class to use the members of a base class provided as a template parameter, without qualifying them? The reason I ask is because doing so would allow me to refactor some code without a LOT of changes.

It can be assumed the type used as the template parameter has all those members, otherwise a compile failure is acceptable.

Neil Kirk
  • 20,002
  • 5
  • 48
  • 79

2 Answers2

5

Introduction

You get the error because the base-class is dependent on a template-parameter, which isn't too surprising since the base-class is the direct use of the template-parameter.

The error diagnostic comes from the fact that different template-parameters could yield significantly different behavior inside the class; what if the passed in template-parameter doesn't have a certain member; are we then to look up something in the global scope?


Explicitly state that you would like to access a member of this

You are saying that you would like to access members of the base-class without qualifying them, and if I were to take you literally on this I would say that you could use this->member-name — but I doubt that this is what you are after given what you wrote about refactoring.

struct A { 
  int m;
};
template<class T>
struct B : T { 
  void func () {
    this->m = 1;
  }
};
int main () {
  B<A> {}.func (); 
}

Bring in names from the dependent base-class

Another alternative is to explicitly state that you would like certain names from your base-class to be available directly in that which derives from it— using using, as in the below:

template<class T>
struct B : T {
  using T::m;

  void func () {
    m = 1;
  }
};

The above can be read as; "dear compiler, wherever I'm referring to m I would like you to use the one in T".


But I want to hack the shit out of this problem; how!?

Alright, introduce a non-dependent base and have that introduce references to the data that you really want. This will work if you know what names that you will want to pull in for every T.

You can even extend this hack to automatically have it deduce the type of those members, but that is far away from the scope of the question.

#include <iostream>

struct A {
  int n;
  int m;

  void print () {
    std::cout << m << std::endl;
  }
};
struct Hack {
  template<class T>
  Hack (T* hck) : m (hck->m), n (hck->n) { }
  int& m;
  int& n;
};
template<class T>
struct B : T, Hack {
  B () : Hack (static_cast<T*> (this)) { }

  void func () {
    m = 123;
  }
};
int main () {
  B<A> b;
  b.func ();
  b.print ();
}

You can find a running example here. Word of warning; I would personally never do this, but as you can see it is possible to do what you ask through a little bit of indirection.

Community
  • 1
  • 1
Filip Roséen - refp
  • 58,021
  • 18
  • 139
  • 186
3

You can add:

using D::a;
using D::b;
using D::g;

to Bob to fix your scoping issue.

Here is a comprehensive overview of this problem. Honestly, it's a corner of C++ that shouldn't exist, but, no language is perfect =P

David
  • 25,830
  • 16
  • 80
  • 130
  • Hm any way around it without needing to add that to many classes? – Neil Kirk Oct 09 '15 at 19:20
  • @NeilKirk Not really, no. You can either add the `using D::`s to the class or in the scope where you use those variables, or refer to the variables using `this->` or `D::` every time. Those are the options. – David Oct 09 '15 at 19:30
  • @NeilKirk see the section titled *"But I want to hack the shit out of this problem; how!?"* in my answer; added just for you. – Filip Roséen - refp Oct 09 '15 at 19:37
  • @FilipRoséen-refp that hack more code and far less clear than just writing `using D::a` for each variable. – David Oct 09 '15 at 19:59
  • @David not if you have a lot of classes where you need that, which I assume since otherwise *OP* is just lazy (if he can't be bothered to change that for a single class). – Filip Roséen - refp Oct 09 '15 at 20:02