views:

145

answers:

6

Here is a simplified version of what I have (not working):

prog.h:

...
const string c_strExample1 = "ex1";
const string c_strExample2 = "ex2";
const string c_astrExamples[] = {c_strExample1, c_strExample2};
...

prog.cpp:

...
int main()
{
    int nLength = c_astrExamples.length();
    for (int i = 0; i < nLength; i++)
     cout << c_astrExamples[i] << "\n";
    return 0;
}
...

When I try to build, I get the following error: error C2228: left of '.length' must have class/struct/union

The error occurs only when I try to use member functions of the c_astrExamples. If I replace "c_astrExamples.length()" with the number 2, everything appears to work correctly.

I am able to use the member functions of c_strExample1 and c_strExample2, so I think the behavior arises out of some difference between my use of strings vs arrays of strings.

Is my initialization in prog.h wrong? Do I need something special in prog.cpp?

A: 

c_astrExamples is an array, there is no "length()" method in it.

marcos
+7  A: 

Arrays in C++ don't have member functions. You should use a collection like vector<string> if you want an object, or compute the length like this:

int nLength = sizeof(c_astrExamples)/sizeof(c_astrExamples[0]);
Grumdrig
@Grumdrig: You need to start a new sentence: '...vector<string>. If you...'
quamrana
@quamrana - Grumdrig's sentences parses correctly for me. "You should use X if you want blah, otherwise do this." Perfectly grammatical construct.
asveikau
I would also note to the questioner than he be careful with sizeof(). It often confuses newbies that it won't tell you the size of anything that the compiler doesn't know, eg. dynamically allocated memory, or something passed as a parameter to a function.
asveikau
For the record, I'm ended up using vectors.
Paul Williams
The problem with vectors is that the question deals directly with constant arrays, and it is not trivial to construct a constant vector (it will be easier in C++0x) Options range from creating array constants and using them as iterators, or creating values within functions that get called at the place of initialization.
David Rodríguez - dribeas
About the calculation of the number of elements in the array: There are other solutions, explicitly search for it. While harder to read initially, there are templated versions, and macros that will backfire if you try to call it on a non-array element (as a pointer). A simple templated version would be: `template <typename T, std::size_t N> inline std::size_t array_size( T ( }`, which to me is preferable over the presented solution (that will fail hard if faced with a pointer instead of an array)
David Rodríguez - dribeas
+1  A: 

Arrays in C++ are inherited from C, which wasn't object-oriented. So they aren't objects and don't have member functions. (In that they behave like int, float and the other built-in types.) From that ancestry stem more problems with array, like the fact that they easily (e.g., when passed into a function) decay into a pointer to the first element with no size information left.

The usual advice is to use std::vector instead, which is a dynamically resizable array. However, if you the array size is known at compile-time and you need a constant, then boost's array type (boost::array, if your compiler supports the TR1 standard extensions also available as std::tr1::array, to become std::array in the next version of the C++ standard) is what you want.

Edit 1:

A safe way to get the length of an array in C++ involves an incredible combination of templates, function pointers and even a macro thrown into the mix:

template <typename T, std::size_t N>
char (&array_size_helper(T (&)[N]))[N];

#define ARRAY_SIZE(Array_) (sizeof( array_size_helper(Array_) ))

If you (like me) think this is hilarious, look at boost::array.

Edit 2:

As dribeas said in a comment, if you don't need a compile-time constant, this

template <typename T, std::size_t N>
inline std::size_t array_size(T(&)[N])
{return N;}

is sufficient (and much easier to read and understand).

sbi
Thanks for the additional note about decaying into a pointer to the first element.That explains some behavior I was getting that for a moment lead me to believe that I had a pointer to an array.
Paul Williams
An important note here is that the template + sizeof() on the template call will provide a compile time constant. There are other simpler, easier to read syntaxes that are safe but yield a runtime value: `template <typename T, std::size_t N> inline std::size_t array_size( T ( }` is safe and in most compilers will get inlined and translated into a constant at compile time (no runtime overhead) but it is not a 'compile time constant' that can be used in metaprogramming.
David Rodríguez - dribeas
+1  A: 

In C++ arrays are not objects and have no methods on it. If you need to get the length of the array you could use the following macro

#define COUNTOF( array ) ( sizeof( array )/sizeof( array[0] ) )
int nLength = COUNTOF(c_astrExamples);
Marcel Gosselin
Note that this macro has the disadvantage that it compile with `array` being a pointer. (Try `sizeof( 0[array] )` instead, which fails with pointers, but works with arrays.)
sbi
@sbi: How should `0[pointer]` fail? That is a valid operation in C++ as far as I know. (And as far as g++ 4.0 considers)
David Rodríguez - dribeas
@dribeas: I think you're right. So scratch that. `:(`
sbi
A: 

Also, beware of initialisation in a header file. You risk offending the linker.
You should have:

prog.h:

extern const string c_strExample1;
extern const string c_strExample2;
extern const string c_astrExamples[];
quamrana
With `const` objects, I don't think this is true.
sbi
@sbi: You're quite right, but it doesn't hurt, in case the `const` is ever removed.
quamrana
@quamrana: Sometimes it _does_ hurt, though, so be careful: In `struct X { static const int k = 5; };`, `X::k` can be used as compile-time constant (e.g., for specifying a stack array's size). If it had been initialized in the class' cpp file instead, this would not be possible. Also, that a global constant is changed to a _global variable_ should raise a few eyebrows anyway. That Paul's code would then fail at link-time probably counts as an advantage.
sbi
+2  A: 

Just use STL vector of strings instead of array:

#include <string>
#include <vector>
using namespace std;

const string c_strExample1 = "ex1";
const string c_strExample2 = "ex2";
vector<string> c_astrExamples;
c_astrExamples.push_back(c_strExample1);
c_astrExamples.push_back(c_strExample2);

int main()
{
    int nLength = c_astrExamples.size();
Igor Oks