Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"typename" and "template" keywords: are they really necessary?

There are a lot of questions at this site with the problems while compiling c++ template code. One of the most common solutions to such problems is to add typename (and, less frequently, template) keyword in the right places of the program code:

template<typename T>
class Base
{
public:

    typedef char SomeType;

    template<typename U>
    void SomeMethod(SomeType& v)
    {
        // ...
    }

};

template<typename T>
class Derived : public Base<T>
{
public:

    void Method()
    {
        typename Base<T>::SomeType x;
    //  ^^^^^^^^

        this->template SomeMethod<int>(x);
    //        ^^^^^^^^
    }
};

Whether there is a code that compiles both with and without keyword typename and gives different results (e.g. output strings)? Similar question for template keyword.

If not, whether these keywords are really necessary in such meanings?

A small overview of the current situation

@Paul Evans wrote a good answer but it is more suitable for the question "Where and why do I have to put the “template” and “typename” keywords?", not for my question.

@Simple gave an example of the required code for typename keyword and its possible variation. @Jarod42 gave another variation without any templates, which is probably a gcc bug because it does not compile with clang.

@n.m. gave an example of the required code for template keyword, @dyp improved it. @n.m. also wrote the another code for both keywords using SFINAE.

@James Kanze in his answer argues that it is impossible to write the required code and any attempts to do it will result in undefined behavior. So the above examples of code are illegal.

It is interesting to find out who is right and what the C++ standard says about this.

like image 546
Constructor Avatar asked Feb 13 '23 16:02

Constructor


2 Answers

The rule is: typename has to be used whenever a name that depends on a template parameter is a type. There are clear cases where it's needed, consider

template <typename T> 
class Foo { 
    typename T::type * p; 
    //... 
}; 

Here, the second typename is used to indicate that type is a type defined within class T. Thus, p is a pointer to the type T::type.

Without typename, type would be considered a member of class T. So the expression:

T::type * p

would be a multiplication of the type member of class T with p.

Similarly, the rule is: .template, ->template or ::template must be used when accessing a template member that uses a template parameter. Consider:

 p->template SomeMethod<int>(x);

without the use of template, the compiler does not know that the < token is not less-than but the start of a template argument list.

like image 106
Paul Evans Avatar answered Mar 02 '23 23:03

Paul Evans


By definition, it is impossible to write code which differs in meaning with or without the keyword; any attempt to do so will result in undefined behavior. The only purpose of these keywords (at least in this context) is to allow the compiler to fully parse the template before instantiation: in order to correctly parse C++, the compiler must know whether a symbol designates a type, a template or something else.

Once you instantiate the template, the compiler no longer really needs the keywords; they have no impact on name lookup, or anything else. The only particularity is that if the name found puts a lie to your declaration (you said type, and in the instantiation, it isn't a type), undefined behavior occurs. If, for example, the compiler has stored the template in the form of a parse tree, this parse tree will be incorrect, and who knows what that might imply.

I would expect that most good compilers would note the category in the first parse, and if name lookup returns something else, emit an error, but for historical reasons, I suspect that there are still compilers which more or less ignore the template or typename in this context, treating it as a comment; store the template as a sequence of tokens, and only parse once the template is instantiated, using the categories it actually finds in the instantiation.

EDIT:

I've been rereading parts of the standard, and I'm no longer sure that there is undefined behavior. C++11, at least, says:

A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified." by the keyword typename.

When a qualified-id is intended to refer to a type that is not a member of the current instantiation and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier. If the qualified-id in a typename-specifier does not denote a type, the program is ill-formed.

Ill-formed usually (but not always) requires a diagnostic. (As mentioned above, I would expect a compiler to issue a diagnostic in any case.)

like image 26
James Kanze Avatar answered Mar 03 '23 00:03

James Kanze