Undefined symbol error for static const char

Discussion in 'C++' started by rawhm, Oct 8, 2009.

  1. rawhm

    rawhm Guest

    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
     
    rawhm, Oct 8, 2009
    #1
    1. Advertising

  2. rawhm

    croberts Guest

    On Oct 8, 8:40 am, rawhm <> wrote:
    > 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


    What version of g++ are you using? I have just compiled your source
    code successfully using g++ 4.3.2.
     
    croberts, Oct 8, 2009
    #2
    1. Advertising

  3. rawhm <> writes:

    > 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



    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)


    --
    __Pascal Bourguignon__
     
    Pascal J. Bourguignon, Oct 8, 2009
    #3
  4. On 8 oct, 13:44, (Pascal J. Bourguignon) wrote:
    > rawhm <> writes:
    > > 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

    >
    > 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.

    --
    Michael
     
    Michael Doubez, Oct 8, 2009
    #4
  5. rawhm wrote:
    > 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



    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?

    --
    Best regards,
    Andrey Tarasevich
     
    Andrey Tarasevich, Oct 8, 2009
    #5
  6. rawhm

    James Kanze Guest

    On Oct 8, 3:20 pm, Pete Becker <> wrote:
    > rawhm wrote:
    > > 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


    > 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.

    --
    James Kanze
     
    James Kanze, Oct 8, 2009
    #6
  7. rawhm

    rawhm Guest

    On Oct 8, 2:05 pm, James Kanze <> wrote:
    > On Oct 8, 3:20 pm, Pete Becker <> wrote:
    >
    >
    >
    > > rawhm wrote:
    > > > 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

    > > 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.


    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.
     
    rawhm, Oct 9, 2009
    #7
  8. On 8 oct, 23:05, James Kanze <> wrote:
    > On Oct 8, 3:20 pm, Pete Becker <> wrote:
    >
    >
    >
    > > rawhm wrote:
    > > > 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

    > > 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.


    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 ?

    --
    Michael
     
    Michael Doubez, Oct 9, 2009
    #8
  9. rawhm

    James Kanze Guest

    > > > > };
    > > > > 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
    > > > 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.


    > 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.)

    --
    James Kanze
     
    James Kanze, Oct 9, 2009
    #9
  10. -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    rawhm wrote:

    > 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


    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-----
     
    Michael Tsang, Oct 12, 2009
    #10
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Thomas Matthews
    Replies:
    5
    Views:
    2,439
    tom_usenet
    Aug 2, 2004
  2. Santa
    Replies:
    1
    Views:
    1,090
    Mark A. Odell
    Jul 17, 2003
  3. Replies:
    24
    Views:
    838
    Netocrat
    Oct 30, 2005
  4. lovecreatesbeauty
    Replies:
    1
    Views:
    1,061
    Ian Collins
    May 9, 2006
  5. Javier
    Replies:
    2
    Views:
    567
    James Kanze
    Sep 4, 2007
Loading...

Share This Page