4

This question is similar to this, but more complex.

there are interfaces and classes:

class B
{
public:
virtual string getName( int index )
    { return names.size() < index ? names[ index ] : string(); };

protected:
    vector<string> names;
};

template< typename T >
class A : public T
{
public:
    string getName( void ) // warning here
      { return name; };

protected:
    string name;
};

int main(int argc, const char * argv[])
{
    A<B> test;
}

As you see i can't to use using T::getName in A because T may not have the getName() method.

How I can to suppress warning:

'A<B>::getName' hides overloaded virtual function

in this case?

Community
  • 1
  • 1
kaa
  • 1,265
  • 5
  • 22
  • 39

3 Answers3

4

The compiler issued a warning, not an error. Your code is technically correct C++, and the compiler has no problem understanding it, and building it. It is, however, suspect, which is why the compiler warns you that you might be doing something you didn't really mean to do.

The issue is explained here

You inherit a function with this signature:

string getName( int index )

But you declare a function by the same name, with a different signature:

string getName( void )

This means that you lose access to the function you have inherited. It cannot be invoked from A<T>. If you tried to invoke test.getName (4); you will get an error, because class A<T> no longer recognized the function getName() with that signature. It has been hidden (as the compiler warned).

Gil Elad
  • 409
  • 2
  • 6
2

The simple answer is to change the name of the getName method in either A or B.

If you really don't want to do that, then don't say I didn't warn you. Your alternative is to override getName(int) in A and simply call its namesake in B:

template< typename T >
class A : public T
{
public:
    string getName() { return name; }
    string getName(int index) { return T::getName(index); }

This should compile even if T doesn't have a getName method, because the compiler will not attempt to instantiate A::getName(int) unless you actually call it.

If you're interested to see how far you could go with this, the following is a version which will make A::getName(int) callable even if T doesn't have a getName method. In this case, it will return the empty string. (I don't recommend actually using this.)

template< typename T, typename F >
class has_getName
{
    struct yes { char x; };
    struct no { yes y, z; };
    template <typename U> static no test(...);
    template <typename U> static yes test(std::integral_constant<F, &U::getName>*);
public:
    typedef std::integral_constant<bool, sizeof(test<T>(0)) == sizeof(yes)> type;
};

template< typename T >
class A : public T
{
    string getNameHelper(int index, std::false_type) { return string(); }
    string getNameHelper(int index, std::true_type) { return T::getName(index); }
public:
    string getName() { return name; }
    string getName(int index) {
        return getNameHelper(index, typename has_getName<T, string(T::*)(int)>::type());
    }

N.B. All your getName methods should really be const as they do not modify the object.

Oktalist
  • 14,336
  • 3
  • 43
  • 63
  • Thanks for the answer. Of course it is right in general purpose. But in the real project I can't apply this suggestions. – kaa Apr 08 '14 at 19:58
1

You can make a specialization of A based on a has_getName<> condition and insert using T::getName in the truth case:

template< typename T, bool = has_getName<T>::value>
class A : public T
{
public:
    using T::getName;
    string getName( void )
      { return name; };

protected:
    string name;
};

template<typename T>
class A<T, false> : public T
{
public:
    string getName( void )
      { return name; };

protected:
    string name;
};
David G
  • 94,763
  • 41
  • 167
  • 253