I need to forward-declare a class in my header file, like this:
class MyStaticClass;
I understand why one cannot forward-declare data members of this class. I used to think that you could however forward-declare functions. I would like to declare a static function of this class, like this:
class MyStaticClass;
static int MyStaticClass::AddTwoNumbers(const int a, const int b);
This gives me a compile error though:
error C2027: use of undefined type 'MyStaticClass'
Why can this not be done? Or is there a secret way of doing this after all?
This is not allowed because it would allow others to add member functions to a class without even editing the class itself.
Consider this,
struct X
{
static void f(float a) { std::cout << a << std::endl; }
private:
static int _data; //inaccessible to non-member
};
X::f(0); //convert 0 (an int) to float, and call X::f().
Now imagine someone came and forward-declare the following function, just before including header which defines the above class:
static void X::f(int);
Now the previous call X::f(0) would give linker error (unresolved name) because now 0 wouldn't convert to float, because it does not need to as there is a declared function which accepts an int, though it is not defined — worse, if it is defined, then you wont even get the linker error and you would probably not easily know that a different function is being called.
Morever. f(int) can now access the private member _data as well — in this way, anyone can access any private/protected members just by adding functions at whim.
Related
I am looking for a portable one line replacement for the #define in the following code. The replacement should hide the word APPLE in the namespace of the Foo object.
class Foo {
public:
#define APPLE 123
Foo(int a) : a_(a) { }
};
// elsewhere in another file
Foo f(APPLE);
I tried to make this more C++ friendly this, and it worked using the Intel 2017 compiler:
class Foo {
public:
static constexpr int APPLE = 123;
Foo(int a) : a_(a) { }
};
// elsewhere
Foo a(Foo::APPLE);
but it does not work with g++ ((GCC) 6.3.1 20170216), because it gives the error
undefined reference to Foo::APPLE
because it is probably trying to take a reference to APPLE.
I know I can "fix" the problem by creating definition in a *.cpp file of
constexpr int Foo::APPLE;
but that violates my ideal of having the #define be replaced by 1 line. My Foo class is header-file only, and now I would need a cpp file just for the definition of Foo::APPLE. I know I could also declare APPLE as a function (static constexpr int APPLE() {return 123;}) but that is a whole lot more typing in the declaration and at every point of use I need to call the function with ().
It seems easier just to use the #define and be done with it. Non-static const int works fine, but APPLE is not usable as an argument to the constructor. Maybe there is a good reason why this is impossible in C++.
Edit: This is not a duplicate of Undefined reference to static constexpr char[]. That question is related to a string and why the particular error message comes up. I am trying to avoid using static linkage all together (I acknowledge in my question that I am aware of how to do static linkage) and I want to do it a "better/cleaner" way, and I see from the Answers that the way that passes my criteria is to use enum.
You've already listed most alternatives in your question. You'll need consider which approach you want to take:
Use inline variable which requires C++17 (your first attempt works implicitly in this standard)
Define the static member in a source file, which you don't want to do
Use an inline static member function instead, which you also don't want
Use a namespace scoped constexpr variable instead of a member
Use a member enum: enum : int { APPLE = 123 };
Use the macro (don't pick this)
In addition to the things mentioned already, there's also "poor man's inline variables":
static constexpr const int& APPLE = std::integral_constant<int, 123>::value;
You define a class template with a constant static data member whose value is what you want. You define that static data member out-of-line - but in the header, since it's a static data member of a class template. In this case, std::integral_constant does all that already, so you don't have to write your own.
Then, you define your actual static data member constant as a constexpr reference to that class template static data member; no out-of-line definition is needed because it is not possible to odr-use a reference initialized by a constant expression.
I'm trying to understand why the following is an error:
class Foobar {
public:
static void do_something();
};
static void Foobar::do_something() {} // Error!
int main() {
Foobar::do_something();
}
This errors with "error: cannot declare member function 'static void Foobar::do_something()' to have static linkage" in g++, and "error: 'static' can only be specified inside the class definition" in clang++.
I understand that the way to fix this is to remove "static" in the definition of do_something on line 6. I don't, however, understand why this is an issue. Is it a mundane reason, such as "the C++ grammar dictates so", or is something more complicated going on?
The keyword static has several different meanings in C++, and the code you've written above uses them in two different ways.
In the context of member functions, static means "this member function does not have a receiver object. It's basically a normal function that's nested inside of the scope of the class."
In the context of function declarations, static means "this function is scoped only to this file and can't be called from other places."
When you implemented the function by writing
static void Foobar::do_something() {} // Error!
the compiler interpreted the static here to mean "I'm implementing this member function, and I want to make that function local just to this file." That's not allowed in C++ because it causes some confusion: if multiple different files all defined their own implementation of a member function and then declared them static to avoid collisions at linking, calling the same member function from different places would result in different behavior!
Fortunately, as you noted, there's an easy fix: just delete the static keyword from the definition:
void Foobar::do_something() {} // Should be good to go!
This is perfectly fine because the compiler already knows that do_something is a static member function, since you told it about that earlier on.
This question is already well answered. Details for static can be read here
Golden Rule:
The static keyword is only used with the declaration of a static member, inside the class definition, but not with the definition of that static member.
For future reference, to have the static method put the static method code above where it is called in the same file and define it as static. Also remove the class reference.
So instead of:
static void Foobar::do_something() {} // Error!
Use:
static void doSomething(){} // the Foobar is removed
It won't be able to access object values but just pass these in as parameters.
When I create a function, I can put the code for it after main if I put the prototype above main. For example,
int myFunction(int a)
{
return(a);
}
would have the prototype..
int myFunction(int a);
above main.
However, I have not been able to get this to work for a class definition.
If I put …
class myClass
{
…
};
below main,
I get an error if I put
class myClass;
above main. The error occurs where the class is used within main, and the error is "unknown type name." That's with the c++ compiler that is part of Xcode.
What kind of prototype should I enter above main if the class definition is below main?
When you call a function and the definition is not available, the compiler doesn't have to know the contents in order to continue evaluating the rest of the code (eg: stack usage at the call site). It only needs to know the function's signature to ensure the correct parameters are going to be passed into the function. The linker will hook up the actual address for the function call after the compiler is done.
However when you are using a class it has to know about the details of it - not just that it exists - because it'll need to ensure proper layout on the stack, what parameters are required for the constructor, etc.
The details of the class' functions are of course like regular functions - it just needs the signature to work with - they can be defined later.
A function in C++ is like a black box to its callers; they just need to know what to pass it and what it returns in order to use it.
Classes, on the other hand, cannot be used in this way, because the compiler needs to know how much space to allocate for them, what the types of their members are, etc.
A class definition is a little different because it can contain member function prototypes and definitions.
If your class definition (usually placed in a .h file) is in the same file, then you'll want it to be above your main(). Functions defined outside of your class definition can be defined after main() as shown below.
class Foo
{
// member function prototype
void func1();
//member function definition inside class
void func2()
{
std::cout << "Hello from func2" << std::endl;
}
};
int main()
{
foo instance;
instance.func1();
instance.func2();
return 1;
}
void Foo::func1()
{
std::cout << "Hello from func1" << std::endl;
}
In a way, a function prototype is the function-equivalent of a complete class definition, not a forward declaration.
so, forward declaration:
class X;
introduces the name X and informs the compiler that it is a class. After seeing this, the compiler will allow you to hold and transfer references and pointers to an X, but not create or copy values (instances) of it, so:
void foo(X&); // is allowed (because it deals in references), but
void foo(X); // is not (it deals in copies of an X)
class definition:
class X { ... };
fully defines X's interface and storage requirements. After this, the compiler will allow you to do anything you like with an X. This is why the class definition generally goes into a header file.
function prototype involving forward-declared classes:
int foo(X&); // X may be forward-declared or defined
This has fully declared the complete shape and behaviour of calling foo(X&). The code at the call-site can be completely compiled.
function prototype involving defined classes:
int foo2(X); // X must be defined
This has fully declared the complete shape and behaviour of calling foo2(X), including the requirements for copying the X onto the stack (for emplacing it there when called with a temporary). The code at the call-site can be completely compiled.
Why having function in my class doesn't change size of this class? This info must be stored somewhere, but where?
You can think of a member function as being just like any other function, except that it has an extra, hidden parameter that takes a pointer to the instance on which the member function was called.
For example, this:
class C
{
void f(int i) { }
};
might be implemented (at least conceptually) as:
void C_f(C* this, int i) { }
If it was a const member function, then the hidden parameter would have the type const C* instead. Note that the situation isn't nearly this simple for virtual member functions.
The sizeof(TheClass) is affected only by the data members within the class, plus the vtable if there is any, plus padding bytes if there is any. So adding a nonvirtual function to your class does not affect its size. And if the class already contains a virtual function, adding a second one would not change sizeof(TheClass) either.
Me think (and I tend to be wrong most of the time) that if you only declare a non-virtual function in a class but no implementation the linker will probably remove it all together.
class Toto
{
int foo();
};
M.
I am trying to do the following: Obtain the address of a member function from a class that was locally defined within a function.
class ConnectionBase
{
};
template class<EventType, SinkType>
class ConnectionImpl : public ConnectionBase
{
public:
typedef void (SinkType::*EventCallback)(EventType const&);
};
template<class EventType>
class Source
{
template <class SinkType>
boost::shared_ptr<ConnectionBase> setupCallback(typename ConnectionImpl<EventType, SinkType>::EventCallback func, SinkType* sink)
{
// do the actual connecting.
}
};
class SomeClass
{
public:
void someFunction(int const& event){}
}
class SomeUnitTest
{
public:
void someTest()
{
class NestedClass
{
public:
void someFunction(int const& event){}
};
NestedClass nc;
//Try#1 - This does not work
setupCallback<int, NestedClass>(&NestedClass::someFunction, &nc);
//Try #2 - This also does not work
setupCallback<int, NestedClass>(&SomeUnitTest::someTest::NestedClass::someFunction, &nc);
//Try #3 - Following the GCC error output, I tried this
setupCallback<int, NestedClass>(&SomeUnitTest::someTest()::NestedClass::someFunction, &nc);
SomeClass sc;
//This works fine, as expected
setupCallback<int, SomeClass>(&SomeClass::someFunction, &sc);
}
};
Try #2 and #3 utterly confuse GCC, it has no idea what I am trying to do.
Try #1 produces a more helpful error message saying no setupCallback exists that takes the form "setupCallback(void (SomeUnitTest::someTest()::NestedClass::SomeFunction::*), etc)
Which is how try #3 was born.
I can't really find a lot of information about classes defined inside a function, does anyone know the correct syntax for this, and maybe have a resource that discusses this topic?
Ok, it appears this is settled, as both posters have pointed out, local classes have no linkage, it can't work. Now knowing this, I found this article that discusses this, for anyone else that runs into this problem and stumbles across this question: http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=420
Edit:
Clarification of setupCallback(), working example with a more regular class
Edit #2:
Updated wording to change "nested" to "local". Added more detail for setupCallback.
Edit #3: Added links to furhter information. Thanks everyone.
I don't know about the syntax problem, the usual access rules should apply - but there is another problem here if that would work as these member functions have no linkage.
To accept local types at all, setupCallback() would have to be a template function - but template type arguments with no linkage are not allowed.
§3.5/8 says:
Names not covered by these rules have
no linkage. Moreover, except as noted,
a name declared in a local scope
(3.3.2) has no linkage.
Members of local classes are not covered there. §9.3/3 clarifies that:
Member functions of a local class
(9.8) have no linkage.
Long story cut short: don't use member functions of a local class as callbacks, use a non-local class instead.
You fist variant is the correct one as long as the specific matter of taking the address is considered. There are no restrictions on taking the address of member functions of local classes. The proper syntax is the usual
&NestedClass::someFunction
ans that's it. You can try saving it in an intermediate pointer in your code
void (NestedClass::*ptr)() = &NestedClass::someFunction;
and I'm sure your compiler will accept it.
However, the problem I suspect exists in your code has absolutely nothing to do with the proper way of taking the address of a member function. It is rather about the way the first parameter of setupCallback is declared. Since you say it works with &SomeClass::someFunction as the first argument, I'd expect setupCallback to be declared as
void setupCallback(void (SomeClass::*cb)(), SomeClass *p); // one possibility
i.e. it is hardcoded to expect a pointer to a member of SomeClass specifically. You cannot supply a pointer to a member of NestedClass instead. NestedClass is completely unrelated to SomeClass and pointers to members of NestedClass are completely incompatible with pointers to members of SomeClass. This is why it won't compile.
Unless there's something you are not showing us (like setupCallback being a function template maybe? Or overloaded for different parameter types?), what you are trying to do is simply impossible to achieve regardless of how you take the member address, as long as NestedClass remains unrelated to SomeClass. Function setupCallback is designed to work with SomeClass and SomeClass only.
Provide more information about setupCallback. How is it declared?
Note that if the setupCallback is declared as a function template parametrized by class type, as in
template <class T> void setupCallback(void (T::*cb)(), T* p);
then you won't be able to use the local class NestedClass as template argument for parameter T. In this case the fact that your NestedClass has no linkage does indeed come into play. But, again, it has nothing to do with taking the member address, but rather caused by the fact that classes with no linkage cannot be used as template arguments in C++.