What Is a Nested Name Specifier

nested-name-specifier

The typename is not needed there, and is therefore not allowed.

MSVC do not parse templates properly until they are actually used, so some errors are not found until later.

typename keyword and nested name specifier

As @MikeSeymour's answer explains, going strictly by the standard (C++11, I don't have a C++14 text on hand), case (1) should actually be erroneous as well - typename prefixing a qualified name can only be used when there's at least one name on the left-hand side of the ::.

However, as pointed out by @hvd in the comments, CWG issue 382 indicates the actual intent is to allow typename before any qualified name, including global-namespace qualification. Since this is what most compilers seem to implement, the rest of this answer follows with this idea.

When viewed like this, it's not that case (2) is a restriction, it's that case (1) is benevolence. The required rule (and its very original wording, I believe) is basically that "if a qualified name which depends on template parameters denotes a type, you must prefix it with typename." For convenience, it is loosened to "typename can be used for any qualified name which dentoes a type, whether it's dependent or not."(1)

In contrast, an unqualified name can never be ambiguous as to whether it refers to a type or not, so it never requires typename and typename is therefore not allowed there.


(1)This loosening is not really explicitly stated in the standard, but follows from the combination of several rules. Basically, wherever a simple-type-specifier (something which denotes a type) is allowed, the grammar also allows a typename-specifier (a qualified name prefixed with typename). The rules for templates (mainly in 14.6) only state that typename is required when a dependent qualified name denotes a type. Since nothing forbids typename in other contexts, it can be used with any qualified name which denotes a type (even outside of template context).

Using a nested name specifier in CRTP

impl_t is an incomplete type in base. You can solve the problem either using another template parameter or using a type traits technique:

template<class>
struct traits;

template<>
struct traits<deriv_t>
{
static constexpr std::size_t i_q = 1;
};

...

void print () {
std::cout << "Base, i_q = " << traits<impl_t>::i_q << std::endl;
}

using vec_t = std::array<double, traits<impl_t>::i_q>;

You don't have complaints in print() because at the point of its instantiation impl_t becomes a complete type.

error: nested name specifier for declaration does not refer into a class, class template or class template partial specialization

As I found out from my colleagues. C++ is a pretty funny language. Mainly due to its long history.
The meaning of the compilation error is that the problematic line of code: h::test (h::TYPE::A);

The compiler parsed as "creating a variable of type h::test with the name h::TYPE::A". And then the compiler goes crazy, because you can't call variables that way.

All this is because someone once decided that it might be convenient to declare variables in this

int (value) style;
value = 42;
std::string (str);
str = "hello";

In other cases, the compiler parses the code correctly - it understands that this is a call to the constructor of the h::text class.

Clang error: Dependent nested name specifier for friend class declaration not supported

You can change the friend declaration only valid for the Base of current template parameter T, i.e. current instantiation.

template<typename T>
class Base {
std::set<int> data_;

public:
Base(const std::set<int>& data): data_{data} {}

friend std::size_t
std::hash<Base<T>>::operator()(const Base<T>& b) const noexcept;

//or just
//friend std::size_t
//std::hash<Base>::operator()(const Base& b) const noexcept;
};

Note that the effect is different with yours. With the above friend declaration, for example, given Base<int>, only std::hash<Base<int>>::operator() is the friend. With template friend declaration in your code, given Base<int>, all the possible instantiations like std::hash<Base<int>>::operator(), std::hash<Base<char>>::operator(), ... are friends.



Related Topics



Leave a reply



Submit