You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
635 lines
20 KiB
635 lines
20 KiB
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -std=c++1y -fms-compatibility -fno-spell-checking -fsyntax-only -verify %s
|
|
|
|
|
|
template <class T>
|
|
class A {
|
|
public:
|
|
void f(T a) { }// expected-note 2{{must qualify identifier to find this declaration in dependent base class}}
|
|
void g();// expected-note 2{{must qualify identifier to find this declaration in dependent base class}}
|
|
};
|
|
|
|
template <class T>
|
|
class B : public A<T> {
|
|
public:
|
|
void z(T a)
|
|
{
|
|
f(a); // expected-warning 2{{use of identifier 'f' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
g(); // expected-warning 2{{use of identifier 'g' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
}
|
|
};
|
|
|
|
template class B<int>; // expected-note {{requested here}}
|
|
template class B<char>; // expected-note {{requested here}}
|
|
|
|
void test()
|
|
{
|
|
B<int> b;
|
|
b.z(3);
|
|
}
|
|
|
|
struct A2 {
|
|
template<class T> void f(T) {
|
|
XX; //expected-error {{use of undeclared identifier 'XX'}}
|
|
A2::XX; //expected-error {{no member named 'XX' in 'A2'}}
|
|
}
|
|
};
|
|
template void A2::f(int);
|
|
|
|
template<class T0>
|
|
struct A3 {
|
|
template<class T1> void f(T1) {
|
|
XX; //expected-error {{use of undeclared identifier 'XX'}}
|
|
}
|
|
};
|
|
template void A3<int>::f(int);
|
|
|
|
template<class T0>
|
|
struct A4 {
|
|
void f(char) {
|
|
XX; //expected-error {{use of undeclared identifier 'XX'}}
|
|
}
|
|
};
|
|
template class A4<int>;
|
|
|
|
|
|
namespace lookup_dependent_bases_id_expr {
|
|
|
|
template<class T> class A {
|
|
public:
|
|
int var;
|
|
};
|
|
|
|
|
|
template<class T>
|
|
class B : public A<T> {
|
|
public:
|
|
void f() {
|
|
var = 3; // expected-warning {{use of undeclared identifier 'var'; unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}}
|
|
}
|
|
};
|
|
|
|
template class B<int>;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace lookup_dependent_base_class_static_function {
|
|
|
|
template <class T>
|
|
class A {
|
|
public:
|
|
static void static_func();// expected-note {{must qualify identifier to find this declaration in dependent base class}}
|
|
void func();// expected-note {{must qualify identifier to find this declaration in dependent base class}}
|
|
};
|
|
|
|
|
|
template <class T>
|
|
class B : public A<T> {
|
|
public:
|
|
static void z2(){
|
|
static_func(); // expected-warning {{use of identifier 'static_func' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
func(); // expected-warning {{use of identifier 'func' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} expected-error {{call to non-static member function without an object argument}}
|
|
}
|
|
};
|
|
template class B<int>; // expected-note {{requested here}}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace lookup_dependent_base_class_default_argument {
|
|
|
|
template<class T>
|
|
class A {
|
|
public:
|
|
static int f1(); // expected-note {{must qualify identifier to find this declaration in dependent base class}}
|
|
int f2(); // expected-note {{must qualify identifier to find this declaration in dependent base class}}
|
|
};
|
|
|
|
template<class T>
|
|
class B : public A<T> {
|
|
public:
|
|
void g1(int p = f1());// expected-warning {{use of identifier 'f1' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
void g2(int p = f2());// expected-warning {{use of identifier 'f2' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} expected-error {{call to non-static member function without an object argument}}
|
|
};
|
|
|
|
void foo()
|
|
{
|
|
B<int> b;
|
|
b.g1(); // expected-note {{required here}}
|
|
b.g2(); // expected-note {{required here}}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
namespace lookup_dependent_base_class_friend {
|
|
|
|
template <class T>
|
|
class B {
|
|
public:
|
|
static void g(); // expected-note {{must qualify identifier to find this declaration in dependent base class}}
|
|
};
|
|
|
|
template <class T>
|
|
class A : public B<T> {
|
|
public:
|
|
friend void foo(A<T> p){
|
|
g(); // expected-warning {{use of identifier 'g' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
}
|
|
};
|
|
|
|
int main2()
|
|
{
|
|
A<int> a;
|
|
foo(a); // expected-note {{requested here}}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
namespace lookup_dependent_base_no_typo_correction {
|
|
|
|
class C {
|
|
public:
|
|
int m_hWnd;
|
|
};
|
|
|
|
template <class T>
|
|
class A : public T {
|
|
public:
|
|
void f(int hWnd) {
|
|
m_hWnd = 1; // expected-warning {{use of undeclared identifier 'm_hWnd'; unqualified lookup into dependent bases of class template 'A' is a Microsoft extension}}
|
|
}
|
|
};
|
|
|
|
template class A<C>;
|
|
|
|
}
|
|
|
|
namespace PR12701 {
|
|
|
|
class A {};
|
|
class B {};
|
|
|
|
template <class T>
|
|
class Base {
|
|
public:
|
|
bool base_fun(void* p) { return false; } // expected-note {{must qualify identifier to find this declaration in dependent base class}}
|
|
operator T*() const { return 0; }
|
|
};
|
|
|
|
template <class T>
|
|
class Container : public Base<T> {
|
|
public:
|
|
template <typename S>
|
|
bool operator=(const Container<S>& rhs) {
|
|
return base_fun(rhs); // expected-warning {{use of identifier 'base_fun' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
}
|
|
};
|
|
|
|
void f() {
|
|
Container<A> text_provider;
|
|
Container<B> text_provider2;
|
|
text_provider2 = text_provider; // expected-note {{in instantiation of function template specialization}}
|
|
}
|
|
|
|
} // namespace PR12701
|
|
|
|
namespace PR16014 {
|
|
|
|
struct A {
|
|
int a;
|
|
static int sa;
|
|
};
|
|
template <typename T> struct B : T {
|
|
int foo() { return a; } // expected-warning {{lookup into dependent bases}}
|
|
int *bar() { return &a; } // expected-warning {{lookup into dependent bases}}
|
|
int baz() { return T::a; }
|
|
int T::*qux() { return &T::a; }
|
|
static int T::*stuff() { return &T::a; }
|
|
static int stuff1() { return T::sa; }
|
|
static int *stuff2() { return &T::sa; }
|
|
static int stuff3() { return sa; } // expected-warning {{lookup into dependent bases}}
|
|
static int *stuff4() { return &sa; } // expected-warning {{lookup into dependent bases}}
|
|
};
|
|
|
|
template <typename T> struct C : T {
|
|
int foo() { return b; } // expected-error {{no member named 'b' in 'PR16014::C<PR16014::A>'}} expected-warning {{lookup into dependent bases}}
|
|
int *bar() { return &b; } // expected-error {{no member named 'b' in 'PR16014::C<PR16014::A>'}} expected-warning {{lookup into dependent bases}}
|
|
int baz() { return T::b; } // expected-error {{no member named 'b' in 'PR16014::A'}}
|
|
int T::*qux() { return &T::b; } // expected-error {{no member named 'b' in 'PR16014::A'}}
|
|
int T::*fuz() { return &U::a; } // expected-error {{use of undeclared identifier 'U'}} \
|
|
// expected-warning {{unqualified lookup into dependent bases of class template 'C'}}
|
|
};
|
|
|
|
template struct B<A>;
|
|
template struct C<A>; // expected-note-re 1+ {{in instantiation of member function 'PR16014::C<PR16014::A>::{{.*}}' requested here}}
|
|
|
|
template <typename T> struct D : T {
|
|
struct Inner {
|
|
int foo() {
|
|
// FIXME: MSVC can find this in D's base T! Even worse, if ::sa exists,
|
|
// clang will use it instead.
|
|
return sa; // expected-error {{use of undeclared identifier 'sa'}}
|
|
}
|
|
};
|
|
};
|
|
template struct D<A>;
|
|
|
|
}
|
|
|
|
namespace PR19233 {
|
|
template <class T>
|
|
struct A : T {
|
|
void foo() {
|
|
::undef(); // expected-error {{no member named 'undef' in the global namespace}}
|
|
}
|
|
void bar() {
|
|
::UndefClass::undef(); // expected-error {{no member named 'UndefClass' in the global namespace}}
|
|
}
|
|
void baz() {
|
|
B::qux(); // expected-error {{use of undeclared identifier 'B'}} \
|
|
// expected-warning {{unqualified lookup into dependent bases of class template 'A'}}
|
|
}
|
|
};
|
|
|
|
struct B { void qux(); };
|
|
struct C : B { };
|
|
template struct A<C>; // No error! B is a base of A<C>, and qux is available.
|
|
|
|
struct D { };
|
|
template struct A<D>; // expected-note {{in instantiation of member function 'PR19233::A<PR19233::D>::baz' requested here}}
|
|
|
|
}
|
|
|
|
namespace nonmethod_missing_this {
|
|
template <typename T> struct Base { int y = 42; };
|
|
template <typename T> struct Derived : Base<T> {
|
|
int x = y; // expected-warning {{lookup into dependent bases}}
|
|
auto foo(int j) -> decltype(y * j) { // expected-warning {{lookup into dependent bases}}
|
|
return y * j; // expected-warning {{lookup into dependent bases}}
|
|
}
|
|
int bar() {
|
|
return [&] { return y; }(); // expected-warning {{lookup into dependent bases}}
|
|
}
|
|
};
|
|
template struct Derived<int>;
|
|
}
|
|
|
|
namespace typedef_in_base {
|
|
template <typename T> struct A { typedef T NameFromBase; };
|
|
template <typename T> struct B : A<T> {
|
|
NameFromBase m; // expected-warning {{found via unqualified lookup into dependent bases}}
|
|
};
|
|
static_assert(sizeof(B<int>) == 4, "");
|
|
}
|
|
|
|
namespace struct_in_base {
|
|
template <typename T> struct A { struct NameFromBase {}; };
|
|
template <typename T> struct B : A<T> {
|
|
NameFromBase m; // expected-warning {{found via unqualified lookup into dependent bases}}
|
|
};
|
|
static_assert(sizeof(B<int>) == 1, "");
|
|
}
|
|
|
|
namespace enum_in_base {
|
|
template <typename T> struct A { enum NameFromBase { X }; };
|
|
template <typename T> struct B : A<T> {
|
|
NameFromBase m; // expected-warning {{found via unqualified lookup into dependent bases}}
|
|
};
|
|
static_assert(sizeof(B<int>) == sizeof(A<int>::NameFromBase), "");
|
|
}
|
|
|
|
namespace two_types_in_base {
|
|
template <typename T> struct A { typedef T NameFromBase; }; // expected-note {{member found by ambiguous name lookup}}
|
|
template <typename T> struct B { struct NameFromBase { T m; }; }; // expected-note {{member found by ambiguous name lookup}}
|
|
template <typename T> struct C : A<T>, B<T> {
|
|
NameFromBase m; // expected-error {{member 'NameFromBase' found in multiple base classes of different types}} expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
};
|
|
static_assert(sizeof(C<int>) != 0, ""); // expected-note {{in instantiation of template class 'two_types_in_base::C<int>' requested here}}
|
|
}
|
|
|
|
namespace type_and_decl_in_base {
|
|
template <typename T> struct A { typedef T NameFromBase; };
|
|
template <typename T> struct B { static const T NameFromBase = 42; };
|
|
template <typename T> struct C : A<T>, B<T> {
|
|
NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}}
|
|
};
|
|
}
|
|
|
|
namespace classify_type_from_base {
|
|
template <typename T> struct A { struct NameFromBase {}; };
|
|
template <typename T> struct B : A<T> {
|
|
A<NameFromBase> m; // expected-warning {{found via unqualified lookup into dependent bases}}
|
|
};
|
|
}
|
|
|
|
namespace classify_nontype_from_base {
|
|
// MSVC does not do lookup of non-type declarations from dependent template base
|
|
// classes. The extra lookup only applies to types.
|
|
template <typename T> struct A { void NameFromBase() {} };
|
|
template <void (*F)()> struct B { };
|
|
template <typename T> struct C : A<T> {
|
|
B<C::NameFromBase> a; // correct
|
|
B<NameFromBase> b; // expected-error {{use of undeclared identifier 'NameFromBase'}}
|
|
};
|
|
}
|
|
|
|
namespace template_in_base {
|
|
template <typename T> struct A {
|
|
template <typename U> struct NameFromBase { U x; };
|
|
};
|
|
template <typename T> struct B : A<T> {
|
|
// Correct form.
|
|
typename B::template NameFromBase<T> m;
|
|
};
|
|
template <typename T> struct C : A<T> {
|
|
// Incorrect form.
|
|
NameFromBase<T> m; // expected-error {{unknown type name 'NameFromBase'}}
|
|
//expected-error@-1 {{expected member name or ';' after declaration specifiers}}
|
|
};
|
|
}
|
|
|
|
namespace type_in_inner_class_in_base {
|
|
template <typename T>
|
|
struct A {
|
|
struct B { typedef T NameFromBase; };
|
|
};
|
|
template <typename T>
|
|
struct C : A<T>::B { NameFromBase m; }; // expected-error {{unknown type name 'NameFromBase'}}
|
|
}
|
|
|
|
namespace type_in_inner_template_class_in_base {
|
|
template <typename T>
|
|
struct A {
|
|
template <typename U> struct B { typedef U InnerType; };
|
|
};
|
|
template <typename T>
|
|
struct C : A<T>::template B<T> {
|
|
NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}}
|
|
};
|
|
}
|
|
|
|
namespace have_nondependent_base {
|
|
template <typename T>
|
|
struct A {
|
|
// Nothing, lookup should fail.
|
|
};
|
|
template <typename T>
|
|
struct B : A<T> { NameFromBase m; }; // expected-error {{unknown type name 'NameFromBase'}}
|
|
struct C : A<int> { NameFromBase m; }; // expected-error {{unknown type name 'NameFromBase'}}
|
|
}
|
|
|
|
namespace type_in_base_of_dependent_base {
|
|
struct A { typedef int NameFromBase; };
|
|
template <typename T>
|
|
struct B : A {};
|
|
template <typename T>
|
|
struct C : B<T> { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
}
|
|
|
|
namespace type_in_second_dependent_base {
|
|
template <typename T>
|
|
struct A {};
|
|
template<typename T>
|
|
struct B { typedef T NameFromBase; };
|
|
template <typename T>
|
|
struct D : A<T>, B<T> { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
}
|
|
|
|
namespace type_in_second_non_dependent_base {
|
|
struct A {};
|
|
struct B { typedef int NameFromBase; };
|
|
template<typename T>
|
|
struct C : A, B {};
|
|
template <typename T>
|
|
struct D : C<T> { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
}
|
|
|
|
namespace type_in_virtual_base_of_dependent_base {
|
|
template <typename T>
|
|
struct A { typedef T NameFromBase; };
|
|
template <typename T>
|
|
struct B : virtual A<T> {};
|
|
template <typename T>
|
|
struct C : B<T>, virtual A<T> { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
C<int> c;
|
|
}
|
|
|
|
namespace type_in_base_of_multiple_dependent_bases {
|
|
template <typename T>
|
|
struct A { typedef T NameFromBase; };
|
|
template <typename T>
|
|
struct B : public A<T> {};
|
|
template <typename T>
|
|
struct C : B<T>, public A<T> { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} expected-warning {{direct base 'A<int>' is inaccessible due to ambiguity:}}
|
|
C<int> c; // expected-note {{in instantiation of template class 'type_in_base_of_multiple_dependent_bases::C<int>' requested here}}
|
|
}
|
|
|
|
namespace type_in_dependent_base_of_non_dependent_type {
|
|
template<typename T> struct A { typedef int NameFromBase; };
|
|
template<typename T> struct B : A<T> {
|
|
struct C;
|
|
template<typename TT>
|
|
struct D : C {
|
|
NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
};
|
|
struct E : C {
|
|
NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
};
|
|
};
|
|
template<typename T> struct B<T>::C : B {
|
|
NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
|
|
};
|
|
template<typename T> struct F : B<T>::C {
|
|
NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}}
|
|
};
|
|
}
|
|
|
|
namespace lookup_in_function_contexts {
|
|
template <typename T> struct A { typedef T NameFromBase; };
|
|
template <typename T>
|
|
struct B : A<T> {
|
|
// expected-warning@+1 {{lookup into dependent bases}}
|
|
static auto lateSpecifiedFunc() -> decltype(NameFromBase()) {
|
|
return {};
|
|
}
|
|
|
|
static void memberFunc() {
|
|
NameFromBase x; // expected-warning {{lookup into dependent bases}}
|
|
}
|
|
|
|
static void funcLocalClass() {
|
|
struct X {
|
|
NameFromBase x; // expected-warning {{lookup into dependent bases}}
|
|
} y;
|
|
}
|
|
|
|
void localClassMethod() {
|
|
struct X {
|
|
void bar() {
|
|
NameFromBase m; // expected-warning {{lookup into dependent bases}}
|
|
}
|
|
} x;
|
|
x.bar();
|
|
}
|
|
|
|
static void funcLambda() {
|
|
auto l = []() {
|
|
NameFromBase x; // expected-warning {{lookup into dependent bases}}
|
|
};
|
|
l();
|
|
}
|
|
|
|
static constexpr int constexprFunc() {
|
|
NameFromBase x = {}; // expected-warning {{lookup into dependent bases}}
|
|
return sizeof(x);
|
|
}
|
|
|
|
static auto autoFunc() {
|
|
NameFromBase x; // expected-warning {{lookup into dependent bases}}
|
|
return x;
|
|
}
|
|
};
|
|
|
|
// Force us to parse the methods.
|
|
template struct B<int>;
|
|
}
|
|
|
|
namespace function_template_deduction {
|
|
// Overloaded function templates.
|
|
template <int N> int f() { return N; }
|
|
template <typename T> int f() { return sizeof(T); }
|
|
|
|
// Dependent base class with type.
|
|
template <typename T>
|
|
struct A { typedef T NameFromBase; };
|
|
template <typename T>
|
|
struct B : A<T> {
|
|
// expected-warning@+1 {{found via unqualified lookup into dependent bases}}
|
|
int x = f<NameFromBase>();
|
|
};
|
|
|
|
// Dependent base class with enum.
|
|
template <typename T> struct C { enum { NameFromBase = 4 }; };
|
|
template <typename T> struct D : C<T> {
|
|
// expected-warning@+1 {{use of undeclared identifier 'NameFromBase'; unqualified lookup into dependent bases}}
|
|
int x = f<NameFromBase>();
|
|
};
|
|
}
|
|
|
|
namespace function_template_undef_impl {
|
|
template<class T>
|
|
void f() {
|
|
Undef::staticMethod(); // expected-error {{use of undeclared identifier 'Undef'}}
|
|
UndefVar.method(); // expected-error {{use of undeclared identifier 'UndefVar'}}
|
|
}
|
|
}
|
|
|
|
namespace PR20716 {
|
|
template <template <typename T> class A>
|
|
struct B : A<int>
|
|
{
|
|
XXX x; // expected-error {{unknown type name}}
|
|
};
|
|
|
|
template <typename T>
|
|
struct C {};
|
|
|
|
template <typename T>
|
|
using D = C<T>;
|
|
|
|
template <typename T>
|
|
struct E : D<T>
|
|
{
|
|
XXX x; // expected-error {{unknown type name}}
|
|
};
|
|
}
|
|
|
|
namespace PR23810 {
|
|
void f(int);
|
|
struct Base {
|
|
void f(); // expected-note{{must qualify identifier to find this declaration in dependent base class}}
|
|
};
|
|
template <typename T> struct Template : T {
|
|
void member() {
|
|
f(); // expected-warning {{found via unqualified lookup into dependent bases}}
|
|
}
|
|
};
|
|
void test() {
|
|
Template<Base> x;
|
|
x.member(); // expected-note{{requested here}}
|
|
};
|
|
}
|
|
|
|
namespace PR23823 {
|
|
// Don't delay lookup in SFINAE context.
|
|
template <typename T> decltype(g(T())) check(); // expected-note{{candidate template ignored: substitution failure [with T = int]: use of undeclared identifier 'g'}}
|
|
decltype(check<int>()) x; // expected-error{{no matching function for call to 'check'}}
|
|
|
|
void h();
|
|
template <typename T> decltype(h(T())) check2(); // expected-note{{candidate template ignored: substitution failure [with T = int]: no matching function for call to 'h'}}
|
|
decltype(check2<int>()) y; // expected-error{{no matching function for call to 'check2'}}
|
|
}
|
|
|
|
// We also allow unqualified lookup into bases in contexts where the we know the
|
|
// undeclared identifier *must* be a type, such as a new expression or catch
|
|
// parameter type.
|
|
template <typename T>
|
|
struct UseUnqualifiedTypeNames : T {
|
|
void foo() {
|
|
void *P = new TheType; // expected-warning {{unqualified lookup}} expected-error {{no type}}
|
|
size_t x = __builtin_offsetof(TheType, f2); // expected-warning {{unqualified lookup}} expected-error {{no type}}
|
|
try {
|
|
} catch (TheType) { // expected-warning {{unqualified lookup}} expected-error {{no type}}
|
|
}
|
|
enum E : IntegerType { E0 = 42 }; // expected-warning {{unqualified lookup}} expected-error {{no type}}
|
|
_Atomic(TheType) a; // expected-warning {{unqualified lookup}} expected-error {{no type}}
|
|
}
|
|
void out_of_line();
|
|
};
|
|
template <typename T>
|
|
void UseUnqualifiedTypeNames<T>::out_of_line() {
|
|
void *p = new TheType; // expected-warning {{unqualified lookup}} expected-error {{no type}}
|
|
}
|
|
struct Base {
|
|
typedef int IntegerType;
|
|
struct TheType {
|
|
int f1, f2;
|
|
};
|
|
};
|
|
template struct UseUnqualifiedTypeNames<Base>;
|
|
struct BadBase { };
|
|
template struct UseUnqualifiedTypeNames<BadBase>; // expected-note-re 2 {{in instantiation {{.*}} requested here}}
|
|
|
|
namespace partial_template_lookup {
|
|
|
|
class Bar;
|
|
class Spare;
|
|
|
|
template <class T, class X = Bar>
|
|
class FooTemplated;
|
|
|
|
class FooBase {
|
|
public:
|
|
typedef int BaseTypedef;
|
|
};
|
|
|
|
// Partial template spec (unused)
|
|
template <class T>
|
|
class FooTemplated<T, Spare> {};
|
|
|
|
// Partial template spec (used)
|
|
template <class T>
|
|
class FooTemplated<T, Bar> : public FooBase {};
|
|
|
|
// Full template spec
|
|
template <class T, class X>
|
|
class FooTemplated : public FooTemplated<T, Bar> {
|
|
public:
|
|
BaseTypedef Member; // expected-warning {{unqualified lookup}}
|
|
};
|
|
}
|