c++ – Calling member_function of a templated base class from a function of a derived templated class

Question:

template <class T>
class Base {
public:
    void f() const { cout << "Base\n"; }
};

template <class Tp >
class D1 : public Base<Tp> {
public:
    void g() const
    {
        f();           // вариант_ошибка
        this->f();     // первый вариант вызова
        Base<Tp>::f(); // второй вариант вызова

    }
};

I know to call f () the first or second option. But why is it a mistake to just call f (); ? After all, it is already clear what is called for a given object, and the base class is instantiated in the same way as the derived one …. What explanation can you give (because I'm not sure that I understand all this correctly and clearly)? …

Answer:

Short answer: because the language standard says so. Base classes that are dependent types are not considered when searching for unqualified names.

Long answer: a type that depends on a template parameter is a dependent type (in this example, Base<Tp> ), and the name nested in a dependent type is a dependent name (in this example, Base<Tp>::f ). At the same time, firstly, the unqualified name f does not show whether the author of the code really wanted to use the dependent name in this context. And second, the unqualified name syntax does not provide any means of resolving ambiguities between different types of dependent names.

The implementation of templates in C ++ critically relies on the fact that the compiler, when translating a template definition, must always be able to unambiguously determine the syntactic meaning of each construct, even before the specific values ​​of the template parameters become known. In particular, the compiler must be able to distinguish declarations from expressions, function names from type names, template names from non-template names, etc. Until the specific values ​​of the template parameters are known, the nature of many of the dependent names is generally not known. Because of this, ambiguities arise that cannot be resolved automatically. Base classes that depend on a template parameter are just an example of a dependent type. The names embedded in them will be dependent names of "unknown nature".

For example, if in the template definition D1<Tp> you refer to nested names from some other template (it doesn't matter whether it's a base class or a completely foreign class) like this

Base<Tp>::a *x;
Base<Tp>::с<int()> y;

the compiler should know immediately when translating your template definition whether the first line is a declaration of a variable x of the "pointer" type (i.e. Base<Tp>::a is a type) or just calculating the product of two variables (i.e. Base<Tp>::a is a variable). And is the second line a declaration of a variable y type Base<Tp>::с<0> (i.e. Base<Tp>::с is a pattern) or just evaluating the expression (Base<Tp>::с < 0) > y (i.e. Base<Tp>::с is a variable)

In your example, the situation is the same: even if the compiler understood that the name f , according to your idea, is a nested name from the Base<Tp> class, it would still not be able to determine what f is during the translation of the template definition. Is it a function name, variable name, or type name? If f is the name of a function or variable, then f() is a function call. If f is a type name, then f() is an expression that creates a temporary object of type f .

Resolving such ambiguities is the responsibility of the user. The user must, if necessary, resolve these ambiguities by explicitly specifying the typename and template keywords. However, this method of disambiguation is only supported when applied to qualified names.

Base<Tp>::f(); // 'f' - имя функции или переменной
typename Base<Tp>::f(); // 'f' - имя типа
typename Base<Tp>::template f<>(); // 'f' - имя шаблона типа
Base<Tp>::template f<>(); // 'f' - имя шаблона функции или переменной

Unqualified names do not support such syntax (possibly due to the presence of other syntactic conflicts, perhaps simply due to the fact that the problem has already been solved more radically).

(Note also that if the compiler considered unqualified names as potentially dependent , then in such contexts it would have to consider all (!) Unqualified names as potentially dependent. For any name can be a member of a specific specialization of the base class. would mean that the user would have to supply the keywords typename and template almost all uses of unqualified type names and templates in such code.)

When using the syntax for accessing a class member (via this-> or (*this). ), There is no ambiguity between types, variables and functions, so in your example it is enough. The ambiguity between templates and non-templates persists, so if your f were a template, you would still have to use the template keyword when referring to f

this->template f<>();

PS The key point, once again, is that the ambiguity problem arises at an early stage, even before the specific values ​​of the template parameters are known, i.e. at a stage in the pattern translation process that is commonly referred to as the first phase of name lookups . The older MSVC ++ compiler (and in compatibility mode with those older versions) did not correctly implement two-phase name lookup – it always postponed this process until the point where the template parameters were already known, i.e. until the second phase of searching for names . In such a situation, the above-described ambiguities, of course, do not arise. For this reason, until recently, the "invisible" names from base classes were perfectly well in the MSVC ++ compiler, and there was no requirement to use the typename and template keywords (as described above).

Scroll to Top