The following code fails to compile as expected:
#include<iostream>
class Enclosing {
int x;
class Nested { int y; };
void EnclosingFun(Nested *n) {
std::cout << n->y; // Compiler Error: y is private in Nested
}
};
However, if I change EnclosingFun into a template member function, the compiler (gcc-7) doesn't complain about accessing y:
#include<iostream>
class Enclosing {
int x;
class Nested { int y; };
template <typename T>
void EnclosingFun(Nested *n, T t) {
std::cout << t << n->y; // OK? Why?
}
};
Is this a bug in gcc? Or does c++ have different access rules for template member function to access nested classes?
Is this a bug in gcc? Or does c++ have different access rules for template member function to access nested classes?
No either.
According to the standard, §17.8.1 Implicit instantiation [temp.inst],
An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class, a static data member of a class template, or a substatement of a constexpr if statement ([stmt.if]), unless such instantiation is required.
That means, Enclosing::EnclosingFun() is not instantiated here. Adding the invocation to it would cause it to be instantiated, then you'll get the error, e.g.
prog.cc:8:30: error: 'int Enclosing::Nested::y' is private within this context
std::cout << t << n->y; // OK? Why?
~~~^
LIVE
It is not a bug in GCC. You do not call your function template, therefore the function is not instantiated, and it does not try to access the private member of the nested class. Try to call your function somewhere in a real function (not template) or try to refer it (implicit instantiation).
A function template by itself is not a type, or a function, or any other entity. No code is generated from a source file that contains only template definitions. In order for any code to appear, a template must be instantiated: the template arguments must be determined so that the compiler can generate an actual function (or class, from a class template).
When code refers to a function in context that requires the function definition to exist, and this particular function has not been explicitly instantiated, implicit instantiation occurs.
template void Enclosing::EnclosingFun(Enclosing::Nested *n, int t); // instantiates the function for the second int parameter
Related
This question already has an answer here:
Why are some functions within my template class not getting compiled?
(1 answer)
Closed 2 years ago.
This C++ code builds successfully:
void h(int *)
{
}
template <typename T>
class A
{
public:
void f()
{
T *val;
h(val);
}
};
int main()
{
A<const int> a;
}
Why?
It is undeniable that A<const int>::f() cannot work.
If you call a.f(), it fails to build as expected.
Why is an instance of A<const int> even allowed to exist then?
I'm not sure how SFINAE applies here.
I'd appreciate a C++ standard quote or a relevant link.
It's allowed to exist because it can still be useful for it to exist.
Consider std::vector, which has a one-argument overload of resize, which default-constructs new elements. Definitely useful! But it's also useful to have a std::vector of some type which isn't default-constructible, when you don't intend to resize it in that way. Forcing all member functions to be available, even those not needed by the user, would artificially restrict the applicability of a class.
There's no sfinae here. If we look closely at the declaration of the function, and not it's implementation:
template <typename T>
class A
{
public:
void f();
};
You can see that there's nothing to tell the compiler that this function should not be part of overload resolution.
If we add a return type containing an expression, this is different:
template <typename T>
class A
{
public:
auto f() -> decltype(void(h(std::declval<T*>())));
};
That's different. Now The compiler need to resolve h(T*) to perform overload resolution. If the substitution fails (the instantiation of the function declaration) then the function is not part of the set.
As for why the code still compiles, take a look at this:
void h(int *)
{
}
template <typename T>
class A
{};
template<typename T>
void f()
{
T *val;
h(val);
}
int main()
{
A<const int> a;
}
It's undeniable that f(A<const int>) cannot work
If you call f(a), it fails to build as expected.
Why is an instance of A<const int> even allowed to exist then?
The answer should become obvious now: the function is never instantiated!
The very same applies for member functions. There is no use to instantiate all member function if you don't use them.
If you look for an official statement that comfirm this behavior, then look no further than the standard itself.
From [temp.inst]/4 (emphasis mine):
Unless a member of a class template or a member template is a declared specialization, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist or if the existence of the definition of the member affects the semantics of the program; in particular, the initialization (and any associated side effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.
I am using VS Express 2013 trying to compile a c++ project. I've created a template class with some functions. The class and its functions are all in one header file. I've included the file, I've used the class, I've called functions from it, and despite all that visual studio won't compile the classes' functions that I'm not using. I've turned off all optimizations. Do I HAVE to use a function that I've written just to see that it compiles or not?
Here is the function:
void remove(ID id)
{
sdfgsdfg456456456456sfdsdf
}
The function shouldn't compile. And indeed the project won't compile if I do use this function, but if I don't use the function the project will compile, even if I use other functions from within this class.
Is there a fix to this? Will the same thing happen if I implement the function in a .cpp file?
Edit: I neglected to mention it is a template class. I've added that information in.
As revealed in comments, the reason this is happening is because remove() is a function in a class template. The compiler only instantiates template code if it is actually used; if you don't call remove(), it can have all the syntax errors you want and nobody will complain.
More formally, § 14.7.1 of the standard states (emphasis mine):
The implicit instantiation of a class template specialization causes
the implicit instantiation of the declarations, but not the
definitions or default arguments, of the class member functions
And later in the same section:
An implementation shall not implicitly instantiate a function
template, a member template, a non-virtual member function, a member
class, or a static data member of a class template that does not
require instantiation.
(the word "implicit" is key here; if you use explicit template instantiation, the compiler will immediately try to instantiate all members using the indicated type(s) and fail if any doesn't compile)
This is not just an optimization; you can exploit this behavior to instantiate class templates with types that only support a subset of the template's operations. For example, suppose you write a template class that will be used with types that support a bar() operation, and in addition, some will also support baz(). You could do this:
template<typename T>
class Foo
{
private:
T _myT;
public:
void bar()
{
_myT.bar();
}
void baz()
{
_myT.baz();
}
};
Now suppose you have these too:
struct BarAndBaz
{
void bar() {}
void baz() {}
};
struct BarOnly
{
void bar() {}
};
This will compile and run just fine:
void f()
{
Foo<BarAndBaz> foo1;
foo1.bar();
foo1.baz();
Foo<BarOnly> foo2;
foo2.bar();
// don't try foo2.baz()!
// or template class Foo<BarOnly>!
}
Consider the following code
#include <iostream>
using namespace std;
struct Printer{
Printer(){
std::cout << "Created\n";
}
};
template<class Derived>
struct InitPrinter{
static Printer p;
};
template<class Derived>
Printer InitPrinter<Derived>::p;
struct MyClass:InitPrinter<MyClass>{
MyClass(){}
};
// Uncomment line below to print out created
//auto& p = MyClass::p;
int main() {
return 0;
}
I expected that this would print out "Created", however, it does not print out anything (tested with MSVC and with ideone gcc c++11). Is this a compiler implementation issue, or is this behavior supported by the standard? If the commented out line is uncommented then it prints out as expected. Is there any way to the static Printer p to be instantiated without requiring either changes to MyClass or extra statements like the auto& p = MyClass::p?
The reason I am interested in this is I am looking to have create a templated base class, that will run some code at startup when it is derived from.
The appropriate quote is [temp.inst]/2
Unless a member of a class template or a member template has been explicitly instantiated or explicitly
specialized, the specialization of the member is implicitly instantiated when the specialization is referenced
in a context that requires the member definition to exist; in particular, the initialization (and any associated
side-effects) of a static data member does not occur unless the static data member is itself used in a way
that requires the definition of the static data member to exist.
emphasis mine.
There's also [temp.inst]/1
The implicit instantiation of a class template specialization causes the implicit
instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, scoped member enumerations, static data members and member templates [...]
and [temp.inst]/10
An implementation shall not implicitly instantiate a function template, [...] or a static data member of a class template that does not require instantiation.
Below code works fine:
template<typename T> class X {};
class A; // line-1
void foo(); // line-2
int main ()
{
X<A> vA;
}
class A {};
void foo() {}
Let line-1 and line-2 are moved inside main(). The function doesn't get affected but the class A forward declaration doesn't work and gives compiler error:
error: template argument for template<class T> class X uses local
type main()::A
What you can observe is happening because, in C++, you can define classes inside functions.
So, if you place class A; in main, you are forward-declaring a class in scope of this function (i.e. class main::A), not in global scope (class A).
Thus, you are finally declaring an object of type X with a template argument of undefined class (X<main::A>).
error: template argument for template class X uses local type main()::A
This is the real problem - using a local type. In C++03 you cannot use local types as template arguments, because nobody had figured out how to name the resulting types.
What if you have several class A in several overloaded functions (again using the same name) - would the resulting X<A>'s then be the same type, or different types? How would you tell them apart?
In C++03 the standard passed on this, and just said "Don't do that!".
The problem was resolved in C++11 by deciding that X<A> using a local type A would be the same as if A had been declared in an anonymous namespace outside of the function, like
namespace
{
class A
{ };
}
int main()
{
X<A> vA;
}
So, with a newer compiler (or using a -std=cpp11 option), you can use a local class, and we also know what it means.
However, forward declaring a type inside a function still doesn't mean the same as forward declaring it in another scope. They will just be different types.
Consider a class like so:
template < class T >
class MyClass
{
private:
static T staticObject;
static T * staticPointerObject;
};
...
template < class T >
T MyClass<T>::staticObject; // <-- works
...
template < class T >
T * MyClass<T>::staticPointerObject = NULL; // <-- cannot find symbol staticPointerObject.
I am having trouble figuring out why I cannot successfully create that pointer object.
The above code is all specified in the header, and the issue I mentioned is an error in the link step, so it is not finding the specific symbol.
"Cannot find symbol staticPointerObject" - this looks like a linker error message. Is it? (Details like this have to be specified in your question).
If it is, them most likely it happens because you put the definition(s) of your static member(s) into an implementation file (a .cpp file). In order for this to work correctly, the definitions should be placed into the header file (.h file).
Again, details like this have to be specified in your question. Without them it turns into a random guess-fest.
I suspect the reason your first example is the following (from the 2003 C++ std document). Note particularly the last sentence -- from your example, there seems to be nothing "that requires the member definition to exist".
14.7.1 Implicit instantiation [temp.inst] 1 Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly
specialized (14.7.3), the class template specialization is implicitly
instantiated when the specialization is referenced in a context that
requires a completely-defined object type or when the completeness of
the class type affects the semantics of the program. The implicit
instantiation of a class template specialization causes the implicit
instantiation of the declarations, but not of the definitions or
default arguments, of the class member functions, member classes,
static data members and member templates; and it causes the implicit
instantiation of the definitions of member anonymous unions. Unless a
member of a class template or a member template has been explicitly
instantiated or explicitly specialized, the specialization of the
member is implicitly instantiated when the specialization is
referenced in a context that requires the member definition to exist;
in particular, the initialization (and any associated side-effects) of
a static data member does not occur unless the static data member is
itself used in a way that requires the definition of the static data
member to exist.
Your first "definition" of static member is but a declaration - here is a quote from the standard.
15 An explicit specialization of a
static data member of a template is a
definition if the declaration includes
an initializer; otherwise, it is a
declaration. [Note: there is no syntax
for the definition of a static data
member of a template that requires
default initialization. template<> X
Q::x; This is a declaration
regardless of whether X can be default
initialized (8.5). ]
The second definition should work. Are you sure you have everything available in one compilation unit? What is teh exact text of error message?
The following compiles/runs with g++ - all in one file
#include <iostream>
template < class T >
class MyClass
{
public:
static T staticObject;
static T * staticPointerObject;
};
template < class T >
T MyClass<T>::staticObject;
template < class T >
T * MyClass<T>::staticPointerObject = 0;
int main(int argc, char **argv)
{
int an_int = 5;
MyClass<int>::staticPointerObject = &an_int;
std::cout << *MyClass<int>::staticPointerObject << std::endl;
char a_char = 'a';
MyClass<char>::staticPointerObject = &a_char;
std::cout << *MyClass<char>::staticPointerObject << std::endl;
}
I have found two solutions. Neither of them are 100% what I was hoping for.
Explicitely initialize the specific instance, e.g.
int * MyClass<int>::staticPointerObject = NULL;
This is not convinient especially when I have a lot of different types.
Wrap the pointer inside the class, e.g.
template < class T >
class MyClass
{
private:
struct PointerWrapper
{
T * pointer;
PointerWrapper( void )
: pointer( NULL )
{ }
};
T staticObject;
PointerWrapper staticPointerObject;
};
...
template < class T >
T MyClass<T>::staticObject; // <-- works fine.
...
template < class T >
MyClass<T>::PointerWrapper MyClass<T>::staticPointerObject; // <-- works fine.
This is a bit of a hassle, but at least usable. Why is it I can instansiate a variable object but not a pointer to a variable object? If anything I would think I'd have more problems the other way around (the compiler knows ahead of time what a pointer looks like, but not what my object looks like).
If someone has a better answer I'd love to see it!
I use the following trick all the time. The idea is to put your static in a function, and access it only from that function. This approach also allows you to avoid the need to declare your static in a .cpp file -- everything can live in the .h file. Following your example code:
template < class T >
class MyClass
{
public:
static T * getObject() {
// Initialization goes here.
static T * object = NULL; // or whatever you want
return pointerObject;
}
};