Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are "using typename" directives not implemented by compilers?

I have some code which looks like this:

namespace myLibrary
{
    class A
    {
    public:
        struct Nested
        {
            ...
        };

        ...
    };
}

In some other parts of the code, I need to access A. Since I like readable code, I also like the using directive:

using myLibrary::A;
...
A a;

Now, at some point I also need to access my nested class, so I want to write something like this:

using myLibrary::A::Nested;

Obviously, the compiler can't know that this is a nested class and not a class member, and gives me an error:

error : using declaration can not refer to class member

What I can't understand is why this does not solve the problem:

using typename myLibrary::A::Nested;

The compiler still gives me the exact same error!

Luckily, I have alternatives:

// Using a typedef
typedef myLibrary::A::Nested Nested;

// Using the new C++11 syntax for type aliasing
using Nested = myLibrary::A::Nested;

but I'd like to understand why the using typename directive did not work. Does it not do what I think it does? Or is it not implemented by compilers? If it is the latter, is there a reason for it?

like image 760
Eternal Avatar asked May 24 '14 17:05

Eternal


2 Answers

There is no "using typename directive". There are using-directives and using-declarations.

You're not using any using-directives, and they aren't relevant to the question in any case (they name namespaces, and can't use the typename keyword).

When using-declarations happen to appear within class templates, and happen to name dependent types, they have to use the typename keyword just like anything else that happens to be in a template and happens to name a dependent type.

For example:

template <typename A, typename R>
class Functor : public std::unary_function<A, R>
{
    using typename std::unary_function<A, R>::result_type;
    using typename std::unary_function<A, R>::argument_type;
 public:
    result_type operator() (argument_type) {}
};

In your situations, the namespace-declaration does not appear to be a part of the body of a class template, in fact it appears to be at namespace scope, in which case attempting to name a class member (doesn't matter if it's a type or a function or whatever) violates 7.3.3[namespace.udecl]/8

A using-declaration for a class member shall be a member-declaration

as the compiler correctly diagnosed with "error : using declaration can not refer to class member"

like image 71
Cubbi Avatar answered Sep 17 '22 20:09

Cubbi


A using declaration referring to a namespace member A may only appear at namespace or local scope.

A using declaration referring to a class member Nested may only appear at class scope (along with member declarations), in a class derived from Nested. In fact, such using declarations are grammatically considered to be member declarations. They are used to adjust access qualification, and (with typename) to allow members of type-dependent bases to be used without qualification. These are specific use cases, so member using declarations are a bit specialized.

A nested class is a member of its enclosing class. You should generally avoid using class enclosures as a substitute for namespaces. Metaprogramming provides some exceptions to the rule, but in those cases you still wouldn't use using declarations, at least at namespace scope, because namespaces cannot be templated. Inheriting from an empty metaprogramming class is viable, but do mind the spaghetti.

The using directive is a different beast. It looks like using namespace and it links one namespace to another as a fallback path in name lookup.

like image 31
Potatoswatter Avatar answered Sep 20 '22 20:09

Potatoswatter