This is excerpt from google's c++ coding guidelines.
How can we use a class Foo in a header
file without access to its definition?
We can declare data members of type Foo* or Foo&.
We can declare (but not define) functions with arguments, and/or
return values, of type Foo. (One
exception is if an argument Foo or
const Foo& has a non-explicit,
one-argument constructor, in which
case we need the full definition to
support automatic type conversion.)
We can declare static data members of type Foo. This is because static
data members are defined outside the
class definition.
What I'm curious about is exception in the second bullet. Why is this so? Why is the full definition needed if we want to support automatic type conversion?
My guess is that compiler needs the full definition of the destination type because of the temporary object that is created in the implicit conversion. Am I guessing correctly? Is there more to it?
EDIT:
As I see it, the exception in the guideline is addressed to situation like this:
class A
{
public:
A( int );
};
class B
{
public:
B( A const &a );
};
int main()
{
B b(2);
}
Here we have only one user-defined implicit conversion (from int to A), and call to constructor that accepts A const &. Only thing that makes sense in this exception is to support direct conversion from e.g. int to A, and then to B via constructor that accepts A const &, allowing client code to use this conversion chain without need to explicitly include header file where A class is declared.
The C++ language doesn't differentiate between code in header files and other file. It does not even require that a header is a file. So purely technically the question is meaningless, but in practice you restrict what you do in header files so as not to run afoul of the One Definition Rule. Without restricting yourself, users would have to be careful to only include the header file in one translation unit. With proper restrictions, the header file can be freely included in multiple translation units.
An incomplete type is one where the size is not known, where sizeof cannot be used.
When the class definition is not known, class Foo is necessarily incomplete.
This means you cannot do things that requires the size to be known. And since incompleteness means that members are not known (they would necessarily be known if the size was known) you can't generally call any members. Exception: you can call the destructor, like in delete pFoo, and the compiler must accept that, but it's Undefined Behavior if class Foo has a non-trivial destructor.
The exception noted in the Google guidelines is, however, meaningless.
EDIT: I discovered that people on SO like it better when things are spelled out in detail, so, adding discussion of why the guideline is meaningless.
The guideline says you can "declare (but not define)" but that "one exception is if an argument Foo or const Foo& has a non-explicit, one-argument constructor".
The declaration does not have anything to do with constructors, which one can affirm by simply trying it out:
#include <iostream>
struct Foo;
Foo bar( Foo const& ); // Declaration of function bar, works fine.
struct Foo
{
int x_;
Foo( int x ): x_( x ) {} // Converting constructor.
};
int main()
{
std::cout << bar( 42 ).x_ << std::endl;
}
Foo bar( Foo const& foo ) { return foo; }
In conclusion, again, the Google guidelines' exception is meaningless.
Cheers & hth.,
Suppose that foo.h knows about Foo declaration only
//foo.h
class Foo;
void f(const Foo &); // It is possible to use the reference.
Full definition is in foo.cpp
// foo.cpp
class CanBeConvertedToFoo;
class Foo
{
Foo (const CanBeConvertedToFoo & x); // implicit constructor
}
class CanBeConvertedToFoo is implicit convertable to Foo;
But it is unknown in some.cpp.
// some.cpp
#include "foo.h"
void g(const CanBeConvertedToFoo & x) {
f(x); // Is it known about implicit conversion ?
}
I don't know whether the exception in the second point is true. Implicit conversions must be know only when a function is called, not when it is declared, so the following works even though C is incomplete while f is declared:
#include <iostream>
class C;
void f(C);
struct C { C(int i) { std::cout << "C(" << i << ")" << std::endl; } };
void f(C c) { std::cout << "f(C)" << std::endl; }
int main() { f(2); }
Related
I got a small problem which i don't understand for now and can't find explanation for it. I read about how to use std::unique_ptr in PIMPL idiom and it works but.. not in one weird situation, which of course occured to me now.
The simplest - i will show a simplified code example which reproduce a problem (Compiling with VS2017 Community).
header.h ##
Forward declaration of Forward class, and template class TestForward which has virtual function returning unique_ptr.
class Forward;
using TestUniquePtr = std::unique_ptr<Forward>;
TestUniquePtr make_ptr();
template<int a>
class TestForward {
public:
virtual TestUniquePtr foo();
};
template<int a>
TestUniquePtr TestForward<a>::foo() {
return make_ptr();
}
forward.h
#include "header.h"
#include <iostream>
class Forward {
public:
~Forward() {
std::cout << "HAAA" << std::endl;
}
};
forward.cpp
#include "forward.h"
TestUniquePtr make_ptr() {
return TestUniquePtr{ new Forward };
}
main.cpp
File which does not compile due to 'can't delete an incomplete type'.
Notice that function foo is not even called in here.
So compiler should try to compile this function in this unit?
If this function is not virtual or TestForward is not a template - it works.
#include "header.h"
int main (int argc, char *argv[]) {
TestForward<3> a;
return 0;
}
I know how can i fix this - by defining my deleter which is not a template, and write its definition in forward.cpp but.. I think this should work, so please help me find out why template+virtual make it not working :(
There is so much going on here that all plays together into this error ...
First, consider this: the C++ standard says that if you do this:
struct Incomplete;
void foo(Incomplete* p) { delete p; }
this is legal, but if the full definition of Incomplete turns out to have a non-trivial destructor, the program has undefined behavior. I believe this solely for compatibility with early C-like C++ programs.
So, to improve the safety of programs, the default deleter of unique_ptr uses a "safe delete", i.e. one that fails to compile for incomplete types. This means that the instantiation of the unique_ptr destructor must be aware of the full definition of the pointed-to class.
In your program, any code that uses the TestUniquePtr destructor must therefore be aware of the full definition of Forward.
TestForward::foo uses the destructor. make_ptr returns an object. foo move-constructs its own return value from this object, and then destroys the source. (In the actual generated code, this is most likely optimized away by the return value optimization, but the code must still be valid without it.)
And where/why is TestForward<3>::foo used? Well, since it is virtual, it must be instantiated whereever the vtable of the class is instantiated. And since it is a template instantiation, the vtable is instantiated wherever the constructor is called (because the constructor needs to write the vtable pointer to the object). And the constructor is called in main.
If foo is not virtual, there's no need to instantiate it. And if TestForward is not a template, I guess you put foo into some separate source file instead of the header, so the error didn't manifest for main.
So how do you fix this?
In a typical Pimpl context, you fix this by tightly controlling who instantiates the destructor of the unique_ptr. You explicitly declare the destructor of the interface class and put the definition into the source file where the impl class definition is known.
If, however, you want to hand out unique_ptrs to your incomplete class as opaque handles, you need to replace the default deleter.
// header.h
class Forward;
struct ForwardDeleter {
void operator ()(Forward* ptr);
};
using TestUniquePtr = std::unique_ptr<Forward, ForwardDeleter>;
// forward.cpp
class Forward { ... };
void ForwardDeleter::operator ()(Forward* ptr) { delete ptr; }
I have the following situation:
class Foo
{
public:
static const Foo memberOfFoo;
........
}
So the thing is I can't initialize it in the same line where I declared it, and, I can't initialize it via Initializations List in the constructor, does anyone know what to do?
Put this outside of the class definition then:
const Foo Foo::memberOfFoo = whateverValue;
That is the definition of Foo::memberOfFoo, which can supply an initializer and has to go into the .cpp file (like any other definition of objects, it can only appear once in the whole program, otherwise you will get linker errors).
Sometimes you will find code that doesn't have definitions for its static data members:
struct A {
// sometimes, code won't have an "const int A::x;" anywhere!
static const int x = 42;
};
Omitting the definition like that is valid only if A::x is never address-taken and never passed to reference parameters. A more formal way to say when it is valid to omit the definition is: "When all uses of A::x immediately read the stored value of A::x". That's the case for many static integer constants.
Class statics other than constant integral types need to/can be initialized at the point of definition. You need to declare your (not so)memberOfFoo somewhere, by adding
const Foo Foo::memberOfFoo = /*construct here*/;
This is how you can implement initialization...
class Foo
{
public:
static const Foo memberOfFoo;
Foo(int, double)
{
...
};
};
const Foo Foo::memberOfFoo(42, 3.141592654);
...
Stroustrup states in C++ Language book that order of definitions in the class does not matter.
Indeed:
class C1 {
int foo() { return bar(); } // where is bar() ?
int bar() { return m_count; } // oh, here is bar(). but where is m_count ?
int m_count; // here is m_count. Better late than never !
}
This compiles. Despite misordering. As promised.
So far, so good.
However, this does not compile:
class C2 {
void baz(Inner *p) {} // we were promised that order does not matter
// is Inner defined ?
struct Inner {}; // yes, Inner is define here.
};
This looks a contradiction to Stroustrup's promise of free ordering inside the class. Quoting Stroustrup: "A member function declared within a class can refer to every member of the class as if the class were completely defined before the member function bodies were considered".
Does anybody know a ref to standard clause that allows C1 and disallows C2 ? I am curious why C2 was disallowed while C1 is allowed. Is it compiler bug that contradicts the standard, maybe ?
Note that the following compiles fine (in VS2008):
class C2 {
void baz() { Inner* i = new Inner(); }
struct Inner {};
};
There are two differences between your two examples. The first uses an undeclared symbol inside the body of a function and the second uses an undeclared type in the signature of the function.
I suspect that my example and your first example both work because the function bodies aren't resolved until after the entire class declaration has been parsed. The function signatures have to make sense as soon as they are encountered.
An identifier that denotes a type must be declared prior to use. This has to do with the complicated structure of C++ grammar. The compiler simply cannot parse certain code if it does not know beforehand which identifiers are types.
You are confusing "declarations" with "definitions". The inner class must be declared - if only forward declared - before it can be used.
The order of definition does not matter, but the order of declaration does.
In C++, a declaration means (roughly) stating the "kind" or type of a symbol:
class A; --> A is of kind "class"
void foo(int); --> foo is a function that takes an int and returns nothing
A definition, however, fully defines what the symbol relates to. As in C, a definition happens to be a declaration too.
Finally, to muddy the waters further, for user-declared types, sometimes a definition is required (complete-type) while sometimes a simple declaration is sufficient...
Now, let us revise your issue, I'll illustrate it with a simple example using C++0x:
struct A {
void foo() { ++count; }
void bar(decltype(count) c); // error: 'count' was not declared in this scope
int count;
};
struct B {
void foo() { Inner a; a.foo(); }
void bar(Inner& i); // error: 'Inner' has not been declared
struct Inner { void foo(); };
};
As you can notice, A::foo is parsed correctly, while A::bar is not.
The issue comes from the fact that the compiler "cheats": the methods body are fully analyzed only once the class has been completely parsed. Therefore it is fine to refer to a yet undeclared type/attribute/function in a function body, but it is not fine to refer to it in a function signature (declaration).
You could say that to the compiler, the code is equivalent to:
struct A {
void foo();
void bar(decltype(count) c); // error: 'count' was not declared in this scope
int count;
};
void A::foo() { ++count; }
struct B {
void foo();
void bar(Inner& i); // error: 'Inner' has not been declared
struct Inner { void foo(); };
};
void B::foo() { Inner a; a.foo(); }
Stroustrup sentence is "easy", but not necessarily accurate. For example, using Inner as an attribute requires that it is fully defined (only complete types may be used as non static attributes), which can cause further grief.
struct C {
struct Inner;
Inner foo; // error: field 'foo' has incomplete type
struct Inner { };
};
Though one could argue that Inner foo is a declaration and thus not covered by Stroustrup's quote.
I think the promise of free ordering inside the class is held if you split declaration and definition (into a header and an implementation file). Once you split them, the order really doesn't matter anymore.
I don't understand the purpose of boost::checked_delete. The documentation says:
The C++ Standard allows, in 5.3.5/5,
pointers to incomplete class types to
be deleted with a delete-expression.
When the class has a non-trivial
destructor, or a class-specific
operator delete, the behavior is
undefined. Some compilers issue a
warning when an incomplete type is
deleted, but unfortunately, not all
do, and programmers sometimes ignore
or disable warnings.
The supplied function and class
templates can be used to prevent these
problems, as they require a complete
type, and cause a compilation error
otherwise.
So the C++ standard allows you to delete incomplete types, which causes undefined behavior if the type has a non-trivial destructor. What? How can an incomplete type have any destructor at all? Isn't an incomplete type just a prototype?
The most common example of an incomplete type is one that has only been declared:
// this file does not include the definition of foo
class foo;
void bad(foo *f)
{
delete f; // undefined behavior if there exists foo::~foo
}
In reality, the definition of foo may look like this:
class foo
{
public:
~foo() { ... };
};
But if the top code has not 'seen' the class definition and just sees the class declaration, the code will compile.
Consider the following:
Foo.h:
#ifndef Foo_H
#define Foo_H
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
class Foo : private boost::noncopyable
{
public:
Foo();
~Foo();
void do_something_interesting();
private:
class Impl; // incomplete type
boost::scoped_ptr<Impl> impl;
};
#endif
Foo.cpp:
#include "Foo.h"
#include <string>
#include <iostream>
class Foo::Impl
{
public:
Impl() : name("Foo::Impl")
{}
void say_hi()
{ std::cout << name << " says hi!" << std::endl; }
std::string name;
};
Foo::Foo()
: impl(new Impl)
{}
Foo::~Foo()
{}
void Foo::do_something_interesting()
{ impl->say_hi(); }
Given this (contrived) example, you cannot inline either Foo::Foo or Foo::~Foo because the type is incomplete. By defining both in a context where the type Foo::Impl is a complete type, you can safely delete the type. boost::checked_delete does this safety check for you, and it's purely a compile-time cost. If you either inline Foo::~Foo or omit it entirely, you will get an error from boost::checked_delete wherever you try to destroy a Foo instance.
C++ allows you to use delete on variables that at the time are pointers to incomplete types.
struct S; // incomplete
int main() {
S* s = NULL;
delete s; // legal
}
The compiler doesn't know at that point what S really is. If it turns out S has a non-trivial destructor, then the compiler is not required to detect that problem.
Practically speaking, what probably happens is that when the compiler encounters the delete instruction on an incomplete type, it fills in a call to what it expects will be the type's ordinary compiler-generate default destructor. And if that's what the destructor turns out to be, then everything's fine. But if it turns out that S has a non-trivial destructor, or if it provides its own special method of deletion, then what the compiler filled in earlier will be wrong. The compiler, however, allowed to assume that it correctly compiled the delete instruction and never look back. When that assumption is wrong, you'll get undefined behavior.
The Boost function ensures that it's called only on complete types, thus avoiding the undefined behavior that may occur on incomplete types.
Is following code legal C++ or not?
class Foo
{
class Bar;
void HaveADrink(Bar &bar);
void PayForDrinks(Bar &bar);
public:
void VisitABar(int drinks);
};
class Foo::Bar
{
public:
int countDrinks;
};
void Foo::HaveADrink(Bar &bar)
{
bar.countDrinks++;
}
void Foo::PayForDrinks(Bar &bar)
{
bar.countDrinks = 0;
}
void Foo::VisitABar(int drinks)
{
Bar bar;
for (int i=0; i<drinks; i++) HaveADrink(bar);
PayForDrinks(bar);
}
Both Visual C++ and GCC accepts it, however the code seems somewhat strange to me and I would hate to have it refused by some future compiler.
Still, the pattern seems useful to me to reduce compile time dependencies - I often use it to declare structs which are used to pass some "context" (a bunch of variables) which are shared between a few functions which all reside in the same cpp file, and this way I do not have to introduce the "context" definition into the public interface.
legal, and indeed usefull to hide implementation details to the outside world.
[edit]
I originally said this was the "pimpl idiom" : http://c2.com/cgi/wiki?PimplIdiom
but I agree that this is just part of pimpl is about. This technique is used by pimpl.
You're "forwarding" class Bar inside of class Foo. Perfectly legal as long as you don't do anything inside the definigino of Foo that would require the sizeof Bar. You can reference Bar using pointer or reference (Bar* or Bar&), but if you declare a data member in Foo such as this:
private:
Bar _bar;
It wouldn't work. The reason is because the definition of Foo must be enough to determine the sizeof Foo. Since the size of Bar is unknown inside the definition of Foo, it would make the size of Foo indeterminate. But using a pointer would work:
private:
Bar* _bar;
Because the sizeof of pointer is the same, and thus known, regardless of how Bar will be later defined.