Making an unknown friend
template<typename T>
class List
{
protected:
class a {
int x;
int y;
private:
friend class b; // <------------ Why this is not an error?
};
template <typename U > class b { //If that is not a error this should be an error
int z;
U y;
};
public:
List() {
a* ptr = (a *)new unsigned char[sizeof(a)];
}
};
int main() {
List<int> mylist;
}
Please go through this link, I have my questions as comments in the code.
I am trying to make another class a friend of my class. But that class is not know at the time of making friend. What is the C++ rule that allows for it.
Later I am defining that class in such a way that, it is incompatible with the friend declaration. Why is that not throwing an error.
Thanks
Yes your code is invalid! This is an interesting show of how templates can change meaning of code in subtle ways. The following code is valid:
class List
{
public:
class a {
typedef int type;
friend class b; // that's fine!
};
template <typename U > class b;
};
class b {
List::a::type an_int; // allowed to access private member
};
Standard says at 7.3.1.2/3
If a friend declaration in a non-local class first declares a class or function83) the friend class or function is a member of the innermost enclosing namespace.
When is it a "first declared class"? It says that too there
When looking for a prior declaration of a class or a function declared as a friend, and when the name of the friend class or function is neither a qualified name nor a template-id, scopes outside the innermost enclosing namespace scope are not considered.
The lookup for "class b" is delegated from 7.1.5.3/2 to 3.4.4 which in turn delegates to unqualified name lookup at 3.4/7. All the question now is whether the template-name "b" is visible in the friend declaration class a. If it isn't, the name is not found and the friend declaration will refer to a new declared class at global scope. 3.3.6/1 about the scope of it says
The potential scope of a name declared in a class consists not only of the declarative region following
the name’s declarator, but also of all function bodies, default arguments, and constructor ctor-
initializers in that class (including such things in nested classes).
Ignoring a few pedantic points that would make this wording not apply to here (which were a defect but are fixed in the C++0x version of that paragraph which also makes this easier to read), this list does not include the friend declaration as an area where that template name is visible.
However, the friend was declared in a member class of a class template. When the member class is instantiated different lookup applies - the lookup for friend names declared in a class template! The Standard says
Friend classes or functions can be declared within a class template. When a template is instantiated, the
names of its friends are treated as if the specialization had been explicitly declared at its point of instantiation.
So the following code is invalid:
template<typename T>
class List
{
public:
class a {
typedef int type;
friend class b; // that's fine!
};
template <typename U > class b;
};
// POI
List<int>::a x;
When that causes List<int>::a to be implicitly instantiated, the name a is looked up at "// POI" as if there would have been an explicit specialization declared. In that case, the template List::b has already been declared, and this lookup will hit it and emit an error because it's a template and not a non-template class.
//Run this- it now will compile for you
template <typename U > class b; //<----- forward declaration
template<typename T>
class List
{
protected:
class a {
int x;
int y;
private:
friend class b<T>; // <------------ Add <T>
};
template <typename U > class b {
int z;
U y;
};
public:
List() {
a* ptr = (a *)new unsigned char[sizeof(a)];
}
};
int main() {
List<int> mylist;
}
The code is ill-formed and Comeau rejects it giving the following error
error: invalid redeclaration of type name "b" (declared at
line 11)
I think this is a bug in g++. Intel C++ rejects it too. You can fix the code by defining class B above A.
template <typename U > class b {
int z;
U y;
};
class a {
int x;
int y;
private:
friend class b<T>;
};
Related
There is a post explaining that a template parameter can be declared as friend with the following syntax:
template <typename T>
class A {
friend T;
};
but what if in some scenarios A needs a fried and in others does not? Is it possible to make T an optional argument?
Is there a better solution than using some kind of FakeClass as T?
EDIT1: I found another solution:
class B {};
template <typename T>
class A {
friend T;
};
template <>
class A<void> {
};
int main()
{
A<B> a1;
A<void> a2;
return 0;
}
but what if A is a complicated class with 300 lines of code? Is there an alternative solution without template specialization?
I think befriending a dummy is the cleanest thing you can do, since you can't inherit or conditionally declare friends. You don't even need to create a new dummy type, since:
[class.friend§3]
If the type specifier in a friend declaration designates a (possibly cv-qualified) class type, that class is declared as a friend; otherwise, the friend declaration is ignored.
Which means that you can pretend to befriend int or even void and they'll play along just fine.
This advice is valid only within the limits of the C++ standard and is not intended as a real life guideline. The poster declines any and all responsibilities concerning the application of the advice.
Try this
template<typename... T>
class A {
private:
static const int i = 10;
public:
friend typename std::conditional<sizeof...(T) == 1, T..., void>::type;
};
class X {
public:
static const int a = A<X>::i;
};
class Y {
public:
static const int a = A<>::i; // error, A::i is private
};
Is this code invalid:
template <class T> struct A;
class C {
template <class T> friend void A<T>::foo();
};
In GCC 6.1.0 it says:
error: member 'void A<T>::foo()' declared as friend before type 'A<T>' defined
template <class T> friend void A<T>::foo();
Clang 3.8.0:
warning: dependent nested name specifier 'A<T>::' for friend class declaration
is not supported; turning off access control for 'C' [-Wunsupported-friend]
And Visual Studio 2015 crashes:
fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'f:\dd\vctools\compiler\cxxfe\sl\p1\c\template.cpp', line 8952)
template <class T> friend void A<T>::foo();
More specifically, is A required to be defined before the friend declaration?
template <class T> struct A;
class C {
static void foo();
template <class T> friend void A<T>::f();
};
template <class T> struct A {
void f() { }
};
If so, why?
You refer to A's member function foo. This function is not known to exist yet, because you only forward declare A.
In other words, you'll have to declare A<T>::foo before C, as the GCC message tries to tell you (the other two are rather cryptic). This means you have to declare the complete interface of A before C, instead of only forward declaring it.
Solution
You can declare f as a friend of C in the following way:
class C;
template <class T> struct A {
void f(C const &c);
};
class C {
int i = 42;
public:
static void foo();
template <class T> friend void A<T>::f(C const &c);
};
template <class T> void A<T>::f(C const &c) { std::cout << c.i << std::endl; }
Live Demo
Justification
According to the standard §3.3.2/p6 Point of declaration [basic.scope.pdecl]:
After the point of declaration of a class member, the member name can
be looked up in the scope of its class. [ Note: this is true even if
the class is an incomplete class. For example,
struct X {
enum E { z = 16 };
int b[X::z]; // OK
};
— end note ]
The diagnostic is rather misleading both for GCC and CLANG. The real problem with your code is not the fact that you're trying to access the definition of an incomplete type, but rather is that you can only refer to a class member name only after it has been declared.
The first problem is (I think) from §9.3/7 [class.mfct] and probably some other places in the standard (see clang message below and 101010's answer):
Previously declared member functions may be mentioned in friend declarations.
This problem is similar to the one from this question.
Since you did not declare A<T>::f before C, you cannot declare it has a friend of C.
But there is an hidden problem behing clang's message, if you make A a non-templated class, the message is different:
Incomplete type A in nested name specifier.
Which is closer to gcc message than the actual one, this is because clang's warning is about something else. Per §14.5.4/5 [temp.friend], the standard allows member of class template to be friend, so this must be valid:
template <typename T>
struct A {
void f ();
};
class C {
template <typename T>
friend void A<T>::f(); // Ok, A<T>::f is already declared
void private_member (); // See below
};
But clang still complains:
warning: dependent nested name specifier 'A::' for friend class declaration
is not supported; turning off access control for 'C' [-Wunsupported-friend]
clang does not support such friend declaration, so it simply turns off access control for C, meaning that:
C c;
c.private_member();
Will be "valid" everywhere, which may not be what you want.
Firstly, Forward declaration of a class is not sufficient if you need to use the actual class type, for example, if you need to use it as a base class, or if you need to use the methods of the class in a method.
Since here you try to use its details "foo()", there is no way compiler knows what is A::foo().. Compiler cannot distinguish if it is a typo (or) actual function.. it needs to know the declaration of A::foo() before you can use it.
If you still want to only to forward declare the class and make it a friend, see if friend classes fit your situation
template <class T> struct A;
class C{
template <class T> friend struct A;
};
I recently changed a class from being templated to not, and discovered that I can no longer omit template arguments when writing a using declaration to inherit a constructor from a templated base class. As long as my class isn't templated I can omit the arguments, as soon as it is I can't. In the compilable snippet below bar represents the class before and buzz represents the class after. I've tested both GCC 5.2 and Clang 3.7 and they have the same behavior. Is this a compiler bug or standard?
#include <iostream>
template<class A, class B>
struct foo {
foo(int x) {
std::cout << x << std::endl;
}
};
struct bar : foo<bar, short> {
using foo::foo; // this appears legal
// using foo<bar, short>::foo; // this works too, thought I would need it
};
template<class X>
struct buzz : foo<buzz<X>, short> {
//using foo::foo; // no longer legal for some reason
using foo<buzz<X>, short>::foo; // now do actually need this
};
int main() {
bar x(3);
buzz<float> y(5);
return 0;
}
This is standard.
N4140 [temp.dep]/3: In the definition of a class or class template, if a base class depends on a template-parameter, the base class
scope is not examined during unqualified name lookup either at the point of definition of the class template
or member or during an instantiation of the class template or member.
For buzz, the base class depends on a template parameter, so the foo unqualified lookup does not examine its scope. That is why you need qualified lookup.
If I want to use a member of a template base class from a template derived class, I have to bring it into scope as such:
template <typename T>
struct base
{
void foo();
};
template <typename T>
struct derived : base<T>
{
using base<T>::foo;
};
Why can't I place this using statement into a local scope, like other using statements?
template <typename T>
struct base
{
void foo();
};
template <typename T>
struct derived : base<T>
{
void f()
{
using base<T>::foo; // ERROR: base<T> is not a namespace
}
};
The purpose of using base<T>::foo in the function scope is that you want to call foo in the function, and since it gives error, you cannot do that.
If you want to call the functon (otherwise why you would do that), then you can do these which are allowed:
this->template base<T>::foo(); //syntax 1
this->base<T>::foo(); //syntax 2 - simple
this->foo(); //syntax 3 - simpler
However, you cannot write this:
foo() ; //error - since foo is in base class template!
//if you write `using base<T>::foo` at class scope, it will work!
Demo at ideone : http://www.ideone.com/vfDNs
Read this to know when you must use template keyword in a function call:
Ugly compiler errors with template
The standard (draft 3225) says in [namespace.udecl]:
A using-declaration for a class member shall be a member-declaration. [ Example:
struct X {
int i;
static int s;
};
void f() {
using X::i; // error: X::i is a class member
// and this is not a member declaration.
using X::s; // error: X::s is a class member
// and this is not a member declaration.
}
— end example ]
A using-directive has no such restriction, however ([namespace.udir]):
when looking up a namespace-name in a using-directive, only namespace names are considered
The following code doesn't compile with gcc, but does with Visual Studio:
template <typename T> class A {
public:
T foo;
};
template <typename T> class B: public A <T> {
public:
void bar() { cout << foo << endl; }
};
I get the error:
test.cpp: In member function ‘void B::bar()’:
test.cpp:11: error: ‘foo’ was not declared in this scope
But it should be! If I change bar to
void bar() { cout << this->foo << endl; }
then it does compile, but I don't think I have to do this. Is there something in the official specs of C++ that GCC is following here, or is it just a quirk?
David Joyner had the history, here is the reason.
The problem when compiling B<T> is that its base class A<T> is unknown from the compiler, being a template class, so no way for the compiler to know any members from the base class.
Earlier versions did some inference by actually parsing the base template class, but ISO C++ stated that this inference can lead to conflicts where there should not be.
The solution to reference a base class member in a template is to use this (like you did) or specifically name the base class:
template <typename T> class A {
public:
T foo;
};
template <typename T> class B: public A <T> {
public:
void bar() { cout << A<T>::foo << endl; }
};
More information in gcc manual.
Wow. C++ never ceases to surprise me with its weirdness.
In a template definition, unqualified names will no longer find members of a dependent base (as specified by [temp.dep]/3 in the C++ standard). For example,
template <typename T> struct B {
int m;
int n;
int f ();
int g ();
};
int n;
int g ();
template <typename T> struct C : B<T> {
void h ()
{
m = 0; // error
f (); // error
n = 0; // ::n is modified
g (); // ::g is called
}
};
You must make the names dependent, e.g. by prefixing them with this->. Here is the corrected definition of C::h,
template <typename T> void C<T>::h ()
{
this->m = 0;
this->f ();
this->n = 0
this->g ();
}
As an alternative solution (unfortunately not backwards compatible with GCC 3.3), you may use using declarations instead of this->:
template <typename T> struct C : B<T> {
using B<T>::m;
using B<T>::f;
using B<T>::n;
using B<T>::g;
void h ()
{
m = 0;
f ();
n = 0;
g ();
}
};
That's just all kinds of crazy. Thanks, David.
Here's the "temp.dep/3" section of the standard [ISO/IEC 14882:2003] that they are referring to:
In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member. [Example:
typedef double A;
template<class T> class B {
typedef int A;
};
template<class T> struct X : B<T> {
A a; // a has typedouble
};
The type name A in the definition of X<T> binds to the typedef name defined in the global namespace scope, not to the typedef name defined in the base class B<T>. ] [Example:
struct A {
struct B { /* ... */ };
int a;
int Y;
};
int a;
template<class T> struct Y : T {
struct B { /* ... */ };
B b; //The B defined in Y
void f(int i) { a = i; } // ::a
Y* p; // Y<T>
};
Y<A> ya;
The members A::B, A::a, and A::Y of the template argument A do not affect the binding of names in Y<A>. ]
This changed in gcc-3.4. The C++ parser got much more strict in that release -- per the spec but still kinda annoying for people with legacy or multi-platform code bases.
The main reason C++ cannot assume anything here is that the base template can be specialized for a type later. Continuing the original example:
template<>
class A<int> {};
B<int> x;
x.bar();//this will fail because there is no member foo in A<int>
VC doesn't implemented two-phase lookup, while GCC does. So GCC parses templates before they are instantiated and thus finds more errors than VC.
In your example, foo is a dependent name, since it depends on 'T'. Unless you tell the compiler where it comes from, it cannot check the validity of the template at all, before you instantiate it.
That's why you have to tell the compiler where it comes from.