Undefined symbol error for static const char

R

rawhm

What gives?
$ cat sc.cpp

#include <vector>

class cls {
public:
static const char foo = 100;
};

int main(int argc, char **argv) {
std::vector<char> vect;

vect.resize(10, cls::foo);

return 0;
}
$ g++ sc.cpp
Undefined symbols:
"cls::foo", referenced from:
__ZN3cls3fooE$non_lazy_ptr in ccB49b7c.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
 
C

croberts

What gives?


#include <vector>

class cls {
public:
    static const char foo = 100;

};

int main(int argc, char **argv) {
    std::vector<char> vect;

    vect.resize(10, cls::foo);

    return 0;

}

Undefined symbols:
  "cls::foo", referenced from:
      __ZN3cls3fooE$non_lazy_ptr in ccB49b7c.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

What version of g++ are you using? I have just compiled your source
code successfully using g++ 4.3.2.
 
P

Pascal J. Bourguignon

rawhm said:
What gives?


#include <vector>

class cls {
public:
static const char foo = 100;
};

int main(int argc, char **argv) {
std::vector<char> vect;

vect.resize(10, cls::foo);

return 0;
}

Undefined symbols:
"cls::foo", referenced from:
__ZN3cls3fooE$non_lazy_ptr in ccB49b7c.o
ld: symbol(s) not found
collect2: ld returned 1 exit status


Anotherway to write it seems to be:

class cls {
public:
static const char foo;
};
const char cls::foo = 100;


and both ways are accepted by my compiler (gcc 4.4.1)
 
M

Michael Doubez

Anotherway to write it seems to be:

class cls {
public:
    static const char foo;};

const char cls::foo = 100;

and both ways are accepted by my compiler (gcc 4.4.1)

This is an error in the OP version, it only declares the static member
but doesn't define it (see §9.4.2/2 of the standard). vector<>resize()
expect a const reference as it second argument which doesn't exists.

Perhaps a feature of C++0x makes it work with 4.4.1.
 
A

Andrey Tarasevich

rawhm said:
What gives?


#include <vector>

class cls {
public:
static const char foo = 100;
};

int main(int argc, char **argv) {
std::vector<char> vect;

vect.resize(10, cls::foo);

return 0;
}

Undefined symbols:
"cls::foo", referenced from:
__ZN3cls3fooE$non_lazy_ptr in ccB49b7c.o
ld: symbol(s) not found
collect2: ld returned 1 exit status


Adding an intializer to a static const member _declaration_ does not
turn it into a _definition_. A separate _definition_ of that static
member is still required, if the member is "used" in the code. What
constitutes a "use" of such a member changed from the "original" C++
specification (1998) to the later ones.

In the original C++ standard, the way you are using 'cls::foo' in your
code does qualify as a "use", so you have to _define_ your 'cls::foo'
separately at namespace scope

const char cls::foo;

If your GCC version adheres to that original standard, that would be the
reason for that legitimate linker error.

In the updated C++ standard the definition of "use" for integral
constants was changed considerably. I'm not sure whether 'cls::foo' is
"used" according to the new definition (can't check right now).

Anyway, what version of GCC are you using?
 
J

James Kanze

And the linker is right: it's not defined. Roughly speaking,
if your code takes its address (and that includes passing it
by reference) you have to define it.
const char cls::foo; // definition
The declaration in the class definition is a declaration, not
a definition.

To which I might add (considering that others have mentionned
that it does compile and link successfully with certain versions
of g++): not providing the definition is undefined behavior; the
compiler doesn't have to reject the code. In fact, your results
are likely to vary depending on whether vector::resize is inline
or not, and the level of optimization.
 
R

rawhm

To which I might add (considering that others have mentionned
that it does compile and link successfully with certain versions
of g++): not providing the definition is undefined behavior; the
compiler doesn't have to reject the code.  In fact, your results
are likely to vary depending on whether vector::resize is inline
or not, and the level of optimization.

OK, I see--it makes sense now. Thank you everyone for your
responses. I'm just surprised I have never run into this before, and
I guess my experience led me to believe that static const integral
members are always treated like literals.
 
M

Michael Doubez

To which I might add (considering that others have mentionned
that it does compile and link successfully with certain versions
of g++): not providing the definition is undefined behavior; the
compiler doesn't have to reject the code.  In fact, your results
are likely to vary depending on whether vector::resize is inline
or not, and the level of optimization.

I wondered about that. There is no mention in the standard of UB in
case of missing declaration. I assumed the code would be incorrect and
the error caught at link time.

I understand that depending on the level of optimisation, a reference
on the actual data might or might not be needed but could it really
happen that a compiler generates code but some memory location is
missing and causing UB ?
 
J

James Kanze

};
I wondered about that. There is no mention in the standard of
UB in case of missing declaration. I assumed the code would be
incorrect and the error caught at link time.

What about §2.2 (paragraphs 2 and 3):

An exprssion is potentially evaluated unless[...] . An
object or non-overloaded function is used if its name
appears in a potentially-evaluated expression. [...]

Every program shall contain exactly one definition of
every non-inline function or object that is used in that
program; no diagnostic required.

That "no diagnostic required" is the signal that the behavior is
undefined.
I understand that depending on the level of optimisation, a
reference on the actual data might or might not be needed but
could it really happen that a compiler generates code but some
memory location is missing and causing UB ?

In practice, I think there will be only two possible behaviors:
the code links and works, or it fails to link with an error
message. The standard has (or had---I think C++0x may introduce
this concept in some places) a concept of "alternate behaviors"
or "limited undefined behavior". All programs basically fall
into four categories: requiring a diagnostic (what happens after
the diagnostic is undefined), undefined behavior (diagnostic not
required, but allowed; anything else goes as well), unspecified
behavior (program must compile and run, but what happens when it
runs is unspecified), implementation defined behavior (must be
documented by the implementation) and fully defined behavior
(although in practice no program totally avoids implementation
defined behavior---main returns an int, and the size and
reprentation of an int are implementation defined). There are a
lot of cases where in practice, the code will either fail to
compiler, or it will compile and work as expected, but the only
category above in which such code fits is undefined behavior, so
that's what the standard calls it. (E.g. including <iostream>
and <vector>, then using an std::eek:stream_iterator is undefined
behavior. In practice, however, if <iostream> or <vector>
include all of <iterator>, it will work as expected; if they
don't, it will fail to compile. And I can't conceive of any
other possible behavior in practice.)
 
M

Michael Tsang

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
What gives?


#include <vector>

class cls {
public:
static const char foo = 100;
};

int main(int argc, char **argv) {
std::vector<char> vect;

vect.resize(10, cls::foo);

return 0;
}

Undefined symbols:
"cls::foo", referenced from:
__ZN3cls3fooE$non_lazy_ptr in ccB49b7c.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

According to ISO/IEC 14882:2009 (draft n2960) Section 9.4.2
[class.static.data]:
2 The declaration of a static data member in its class definition is not a
definition and may be of an incomplete type other than cv-qualified void.
The definition for a static data member shall appear in a namespace scope
enclosing the member’s class definition.

You missed the definition so the linker complains.

3 If a static data member is of const literal type, its declaration in the
class definition can specify a brace-or-equal-initializer in which every
initializer-clause that is an assignment-expression is a constant
expression. A static data member of literal type can be declared in the
class definition with the constexpr specifier; if so, its declaration shall
specify a brace-or-equal-initializer in which every initializer-clause that
is an assignment-expression is a constant expression. [ Note: In both these
cases, the member may appear in constant expressions. — end note ] *The
member shall still be defined in a namespace scope if it is used in the
program and the namespace scope definition shall not contain an
initializer.*

You have used the member in the program so a definition is still needed and
the initialization needs to be done in the definition.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)

iEYEARECAAYFAkrTPygACgkQG6NzcAXitM/RuACdHN42LwvnL9R+NhdEKis7MV3u
tB8An1740a3+k4C4U6vV3Pp9oZPXvDgI
=3mOx
-----END PGP SIGNATURE-----
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top