Template + Friend (a deadly combination) [duplicate] - c++

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Making an undefined class as friend, and defining it later.
I have the following code
template<typename T>
class A
{
class B;
B b;
};
int main()
{
return 0;
}
The code doesn't emit any error because A is not instantiated. Am I right?
But have a look at the second code sample
template<typename T>
class A
{
protected:
class a
{
int x;
int y;
private:
friend class b;
};
template <typename U > class b
{
int z;
U y;
};
};
int main()
{
A<int> a;
return 0;
}
In this case A is instantiated but b is not. So the code shouldn't emit any error, right? But I get a redeclaration error in msvc++2008 professional edition. It compiles fine on gcc.
What could be the reason for the error?
Sorry everyone. After reading the comments and after seeing my original code I see that I made a mistake while typing the second code sample.
I missed the definition of the constructor and the statement a *object = new a; inside the constructor. Apologies. That means my code is exactly similar to the other code posted by the other user. But now I cannot delete my question now because of the existing answers.

Regarding you second code snippet ...
template<typename T>
class A
{
protected:
class a
{
int x;
int y;
private:
friend class b;
};
template <typename U > class b
{
int z;
U y;
};
};
int main()
{
A<int> a;
return 0;
}
... it is most probably invalid code, because a very similar code snippet posted by you earlier, in the SO question "Making an undefined class as friend, and defining it later", is invalid. But I'm not sure about this.
The difference wrt. your earlier question is only that in that other question the nested class a is used, hence, instantiated.
However, the redeclaration error that you get with MSVC 8.0 does not necessarily mean that that compiler recognizes the code as invalid standard C++. It might be a compiler bug that just by happenchance causes it to (probably) correctly identify the code as invalid. And it might just be that the above code, not using a, is valid…
So, it's pretty subtle, it's language lawyer stuff.
Best advice is to just stay well clear of these rather dark corners of the language.
EDIT: the earlier question was not Pavel's (but it's the same code); see comments.
Cheers & hth.,

Class a doesn't has an access to the members of the enclosing class according to the current Standard §11.8. But the Standard has a defect report to this. So some compilers works according to the original Standard, and the others according to the proposed resolution of the defect report.

MSVC requires the full template decl for friend template classes, MSDN has an example here, specifically the bottom example:
template <class T>
class X
{
private:
T* data;
void InitData(int seed) { data = new T(seed); }
public:
void print() { cout << *data << endl; }
template <class U> friend class Factory;
};
template <class U>
class Factory
{
public:
U* GetNewObject(int seed)
{
U* pu = new U;
pu->InitData(seed);
return pu;
}
}
so yours should declare:
template <typename U > friend class b;

The code doesn't emit any error
because A is not instantiated. Am I
right?
I am not sure, but I don't think so. A compiler which emits an error is also equally standards conformant as a compiler which does not.
The below quote along with the example that immediately follows is a proof of this to my mind
$14.6/7 - "[Note: if a template is
instantiated, errors will be diagnosed
according to the other rules in this
Standard. Exactly when these errors
are diagnosed is a quality of
implementation issue. ]"

Related

Why class::class::class::staticClassMember() compiles (in C++)?

I must have missed something in C++ specification because I can't explain why the following code compiles successfully:
class MyClass { static void fun(); };
int main() { MyClass::MyClass::MyClass::fun(); }
Could somebody point me to the standard or just explain me the semantics? I would guess that only one MyClass:: is allowed. Two MyClass::MyClass:: should cause error. Experimenting with MS Visual C++ 2017 and GNU C++ 6.2.0 I realized that any count of MyClass:: is allowed.
It is not only a theoretical question. I wanted to use SFINAE and condition compilation with existence of a sub-class. Worked good until the base class has the same name as the sub-class:
template <class T> void callWorkout() { T::SubClass::workout(); }
struct X { struct SubClass { static void workout(); }; };
struct Y { /*empty*/ };
struct SubClass { static void workout(); };
int main() {
callWorkout<X>(); // works fine - compiled
callWorkout<Y>(); // works "fine" - not compiled, no SubClass in Y
callWorkout<SubClass>(); // ooops? - still compiled, there is no 'SubClass' in SubClass
}
My question has two parts:
What is the exact semantics of MyClass::MyClass::?
How can I fix the above example not to compile callWorkout<SubClass>()? (I tried to add sizeof(typename T::SubClass) but surprisingly it compiles also for T=SubClass)
That's the injected class name of MyClass. And you can verify it's not T by simply using std::is_same_v<T, typename T::SubClass> in a SFINAE conditional.
template <class T>
auto callWorkout() -> std::enable_if_t<!std::is_same_v<T, typename T::SubClass>>
{ T::SubClass::workout(); }
If you don't need SFINAE (because you aren't trying to control overload resolution), then a static_assert with a descriptive custom message can also do nicely.

what's the situation that you must use "this" pointer? [duplicate]

This question already has answers here:
Is there any reason to use this->
(16 answers)
Closed 7 years ago.
I have a code working like:
class base {
....
protected:
typeA m_mem;
}
class mymodule: public base{
....
void function(){
m_mem.call();
}
}
This was working OK before. Suddenly, I see it brokes saying "m_mem was not declared...." It might be some other people changed the namespace or other parts.
And I found it was working by just adding "this" then it compiles fine
this->m_mem.call()
While I just would like to know what's the cases that I must use "this" ? I learned "this" can be used to point to distinguish between class member and argument names. While for my case, what can be the reason that I must use "this" for accessing a data member
This can occur for example when you use templates.
The compiler will issue an error that x is an undefined variable for these class definitions
template <class T>
struct A
{
int x;
};
template <class T>
struct B : A<T>
{
void set_x( int v ) { x = v; }
};
If you write
void set_x( int v ) { this->x = v; }
the code will compile.

template circular dependency issue

second template question of the day, what a n00b:
I have a template class:
template <class T>
class foo{
private:
//...
T SubFoo;
//...
};
I also have a class called myClass. I would like to have objects of the kind:
foo<myClass> myObject;
But, and here's the problem, I would like to be able to get a pointer to myObject from myObject.SubFoo. That means that one of the members of the class myClass should be an instantiation of the template class foo.
So I can do:
class myClass{
//...
foo<myClass>* point2myClass;
}
However, it seems that this does not work because
./foo.h:103: error: ‘foo::SubFoo’ has incomplete type
When defining myClass, the program finds the line
foo<myClass>* point2myClass;
It goes to the defintion of foo and it finds:
T SubFoo;
but T, in this case myClass, has not yet been defined (that is what the program was doing!), so it doesn't know what T is, hence the error.
If I interchange the order of declarations, it will also fail because "foo" will not be defined.
How can I make this work??
Thanks a million!
The following code, should definitely work just fine. If your code is different, please specify where.
template < typename T >
struct A
{
T x;
};
struct X
{
A<X>* x;
};
int main()
{
X a;
}

typename when defining map data that is a function pointer with a sprinkling of templates

This is a strange question because I already know the 'coding' answer. I just want to get a better understanding of why it is so. There are guru's here who have a knack of explaining these things better than the C++ standard :)
Below we have a means to define an abstract factory template that allocates objects based on a string as a key (it is a contrived example):-
#include <iostream>
#include <map>
#include <string>
using namespace std;
template <typename T, typename TProduct>
TProduct *MyFactoryConstructHelper(const T *t)
{
if (!t) return new T;
return new T(*static_cast<const T*>(t));
}
template <typename TProduct>
class AbstractFactory
{
public:
typedef TProduct *(*MyFactoryConstructor)(const void *);
typedef map<string, MyFactoryConstructor> MyFactoryConstructorMap;
static TProduct *Create(const string &iName)
{
MyFactoryConstructor ctr = mTypes[iName];
TProduct *result = NULL;
if(ctr) result = ctr(NULL);
return result;
}
template <typename T>
static bool Register(const string &iName) {
typedef TProduct*(*ConstructPtr)(const T*);
ConstructPtr cPtr = MyFactoryConstructHelper<T, TProduct>;
string name = iName;
mTypes.insert(pair<string,MyFactoryConstructor>(name, reinterpret_cast<MyFactoryConstructor>(cPtr)));
return(true);
}
protected:
AbstractFactory() {}
static MyFactoryConstructorMap mTypes;
};
template <typename TProduct>
map<string, /*typename*/ AbstractFactory<TProduct>::MyFactoryConstructor> AbstractFactory<TProduct>::mTypes;
Here is an example of how we use it: -
class MyProduct
{
public:
virtual ~MyProduct() {}
virtual void Iam() = 0;
};
class MyProductFactory : public AbstractFactory<MyProduct>
{
public:
};
class ProductA : public MyProduct
{
public:
void Iam() { cout << "ProductA" << endl; }
};
class ProductB : public MyProduct
{
public:
void Iam() { cout << "ProductB" << endl; }
};
int _tmain(int argc, _TCHAR* argv[])
{
MyProduct *prd;
MyProductFactory::Register<ProductA>("A");
MyProductFactory::Register<ProductB>("B");
prd = MyProductFactory::Create("A");
prd->Iam();
delete prd;
prd = MyProductFactory::Create("B");
prd->Iam();
delete prd;
return 0;
}
It will not compile as is, complaining that the map does not have a valid template type argument for the data type. But if you remove the comments around the 'typename' keyword in the static member definition, everything compiles and works fine... why?
and also, can I make this any better? :)
The standard tries to allow an implementation to parse and
detect as many errors in a template as possible when it reads
the template definition, before any instantiations. C++ is not
context independent, however, and it's very difficult, if not
impossible, to correctly parse statements if you don't know
which symbols are types and which are templates. If the symbol
is dependent (depends on the template parameters in some way),
you have to tell the compiler when it is a type or a template;
otherwise, the compiler must assume that it is something else.
In this case, you're telling the compiler that
AbstractFactory::MyFactoryConstructor names a type,
and not something else.
If, when the template is instantiated, and the compiler can see
to what the symbol is really bound, it turns out that you lied
(e.g. AbstractFactory::MyFactoryConstructor is in fact
an int), then the compiler will get mad at you.
Note too that the fact that the AbstractFactory was defined
before the definition requiring the typedef doesn't change
anything. There could always be an explicit specialization for
the type you're instantiating AbstractFactory on.
The simple reason is that even though you and I looking at the code know that AbstractFactory::MyFactoryConstructor is a type, the compiler doesn't -- or rather, is prohibited by the standard from knowing this. As far as it knows in the first pass of compilation, MyFactoryConstructor -- itself inside a template yet to be fully realized -- could be something else, like a static variable, which isn't allowed as the second template argument to the map, which requires a type. Supplying "typename" permits the compiler to treat it as a type as soon as it is first encountered.

C++ Nested classes forward declaration error

I am trying to declare and use a class B inside of a class A
and define B outside A.
I know for a fact that this is possible because Bjarne Stroustrup
uses this in his book "The C++ programming language"
(page 293,for example the String and Srep classes).
So this is my minimal piece of code that causes problems
class A{
struct B; // forward declaration
B* c;
A() { c->i; }
};
struct A::B {
/*
* we define struct B like this becuase it
* was first declared in the namespace A
*/
int i;
};
int main() {
}
This code gives the following compilation errors in g++ :
tst.cpp: In constructor ‘A::A()’:
tst.cpp:5: error: invalid use of undefined type ‘struct A::B’
tst.cpp:3: error: forward declaration of ‘struct A::B’
I tried to look at the C++ Faq and the closeset I got was here and here but
those don't apply to my situation.
I also read this from here but it's not solving my problem.
Both gcc and MSVC 2005 give compiler errors on this
The expression c->i dereferences the pointer to struct A::B so a full definition must be visible at this point in the program.
The simplest fix is to make the constructor of A non-inline and provide a body for it after the defintion of struct A::B.
Define the constructor for A AFTER the definition of struct B.
This is a good example of why you want to keep definitions separate from declarations. You need to change the order of things so that the constructor A::A() is defined after the definition of struct A::B.
class A
{
struct B;
B* c;
A();
};
struct A::B
{
int i;
};
A::A() { c->i; }
int main()
{
return 0;
}
Interestingly, I've bumped into the same problem with the page 293 ('11.12 A String Class') mentioned in the Stroustrup book.
The example provided in the printed book seems to be at fault, providing the following methods as inline, instead of defining them after the definition of struct Srep
class String {
// ...
void check(int i) const { if (i<0 || rep->sz <=i) throw Range(); }
char read(int i) const { return rep->s[i]; }
void write(int i, char c) { rep=rep->get_own_copy(); rep->s[i]=c; }
...etc...
I googled a bit, and found the author's latest implementation of this String Class, available here:
http://www2.research.att.com/~bs/string_example.c
He seems to have modified it so that these methods are no longer inline, to avoid the problem mentioned in this thread.