0

I have this C++ snippet that I am trying to understand:

in .hh file:

class A
{
private:
    //recordDelimiter is a '+' character
    // I can do this because char is an integral type!
    static const char recordDelimiter = '+';

    void f()
    {
        ....
        //serializedData is a std:;string
        //Get number of times A::recordDelimiter is found i.e. Number of objects

        // Non-Functional
        int times = (int) std::count (serializedData.begin(), serializedData.end(), A::recordDelimiter);

        // Functional
        const char recDel = A::recordDelimiter;
        int times = (int) std::count (serializedData.begin(), serializedData.end(), recDel);

        // Functional
        int times = (int) std::count (serializedData.begin(), serializedData.end(), '+');

        ....
    }
};

From std::count reference this is the signature of the function:

template <class InputIterator, class T>
  typename iterator_traits<InputIterator>::difference_type
    count (InputIterator first, InputIterator last, const T& val)

So I don't see why using A::recordDelimiter instead of '+' is a problem. Compiling gives me undefined reference toA::recordDelimiter'`

So my question is basically, why is my non-functional code above not functional? and how come doing this :

const char recDel = USerializer::recordDelimiter; 

and then pass it to std::count works?

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
Kam
  • 5,878
  • 10
  • 53
  • 97

2 Answers2

2

You have declared, but not defined, A::recordDelimiter. Add this line to exactly one source code file:

const char A::recordDelimiter;

The shortcut of initializing static const members but not defining it can be thought of as providing a value, but not an actual object for the member. So this makes sense:

char foo = A::recordDelimiter; // just substitute '+' for the rhs here

but this this doesn't:

char *foo = & A::recordDelimiter; // Oops, no such object, so it can't have an address

In the case in the question, std::count accepts a const T& as a parameter. Since the object is declared but not defined, it doesn't exist. Therefore, it makes no sense to bind a reference to a non-existing object.

To be clear, it is legal to provide an initializer here, and to never define the object. But you can't take the address of such a declared-and-initialized-but-not-defined object nor bind it to a reference.

Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • Freaking great. +1 because you beat me by 10 sec. – phonetagger Jan 29 '13 at 20:14
  • Apparently whomever -1'ed me also -1'ed you. Real nice. – phonetagger Jan 29 '13 at 20:16
  • How is it that he did not define it? It says `= '+'` in the class which means it was defined right? – David G Jan 29 '13 at 20:23
  • No Guys, it is declared and defined! char is an integral type so static const char recordDelimiter = '+'; should work. btw, I don't have a cpp file for this code. – Kam Jan 29 '13 at 20:26
  • No, @David the `= '+'` is *not* a definition. It is still a declaration. – Robᵩ Jan 29 '13 at 20:35
  • @Rob, please take a look at the update in my post. if I capture the A::delimiter in a char then I pass it to std::count it works. How come? – Kam Jan 29 '13 at 20:36
  • No, @Kam, it is not defined. It is only declared. As a special exception to the one-definition rule, a declared-but-not-defined static const member may be used anywhere that requires a constant integral expression. That exception doesn't apply, since that context does not require a constant integral expression. – Robᵩ Jan 29 '13 at 20:37
  • ahh I see! Thank you! I thought defining something meant, giving it a value :) – Kam Jan 29 '13 at 20:40
  • @Kam - When you define this in a source-code file, it doesn't have to be named A.cpp. You can put that one line anywhere, as long as it appears exactly once in exactly one source-code file. – Robᵩ Jan 29 '13 at 20:46
  • I found this maybe related to this question: [link](http://stackoverflow.com/questions/272900/c-undefined-reference-to-static-class-member) – Guangchun Jan 29 '13 at 21:01
2

Ah! You declared A::recordDelimiter, but never defined it. Is this in a header file? If so, you'll need to add a corresponding definition for it in a source file.

phonetagger
  • 7,701
  • 3
  • 31
  • 55