static const int problem

M

mati

Hi

The following code works:

#include <vector>
class C {
private:
static const int m_static = 2;
public:
void f(const std::vector<int>& v)
{
int a = m_static;
std::vector<int> stripped(v.begin()+a, v.end());
//std::vector<int> s2(v.begin()+m_static,v.end());
}
};
int main()
{
C c;
std::vector<int> pv;
int i;
pv.push_back(i);
pv.push_back(i);
pv.push_back(i);
c.f(pv);
}


But when I erase the comment in the void f(...), then compiler gives an
error:

g++ -ansi -Wall -o test test.cpp
/tmp/cckLnGUY.o: In function `C::f(std::vector said:
> const&)':
test.cpp:(.text._ZN1C1fERKSt6vectorIiSaIiEE[C::f(std::vector<int,
std::allocator<int> > const&)]+0xdb): undefined reference to `C::m_static'
collect2: ld returned 1 exit status
make: *** [test] Error 1

g++ --version
g++ (GCC) 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)


Can anybody tell me what I'm doing wrong?
 
Z

Zeppe

mati said:
Hi

The following code works:

#include <vector>
class C {
private:
static const int m_static = 2;
public:
void f(const std::vector<int>& v)
{
int a = m_static;
std::vector<int> stripped(v.begin()+a, v.end());
//std::vector<int> s2(v.begin()+m_static,v.end());
}
};
int main()
{
C c;
std::vector<int> pv;
int i;
pv.push_back(i);
pv.push_back(i);
pv.push_back(i);
c.f(pv);
}


But when I erase the comment in the void f(...), then compiler gives an
error:

g++ -ansi -Wall -o test test.cpp
/tmp/cckLnGUY.o: In function `C::f(std::vector said:
const&)':
test.cpp:(.text._ZN1C1fERKSt6vectorIiSaIiEE[C::f(std::vector<int,
std::allocator<int> > const&)]+0xdb): undefined reference to `C::m_static'
collect2: ld returned 1 exit status
make: *** [test] Error 1

g++ --version
g++ (GCC) 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)


Can anybody tell me what I'm doing wrong?

Even if you perform the initialization inside the class declaration, you
still need the definition of the static member variable on a cpp file.
Strangely enough, visual studio compiles the code even without it.

Regards,

Zeppe
 
H

Hari

Even if you perform the initialization inside the class declaration, you
still need the definition of the static member variable on a cpp file.
Strangely enough, visual studio compiles the code even without it.

Regards,

Zeppe

Hmmm. I just tested code at: http://dinkumware.com/exam/Default.aspx
on VC8, EDG and MINGW and every compiler make executable files. Maybe -
ansi switch is using this error ?!
 
L

Lionel B

Hi

The following code works:

Um... no:

#include <vector>
class C {
private:
static const int m_static = 2;
public:
void f(const std::vector<int>& v)
{
int a = m_static;
std::vector<int> stripped(v.begin()+a, v.end()); //std::vector<int>
s2(v.begin()+m_static,v.end());
};

What's s2 ? ... ah, if you remove the "//" then suddenly it's a std::vector<int> ... and all compiles ok.
 
L

Lionel B

Um... no:

main.cpp: In member function ‘void C::f(const std::vector<int,


What's s2 ? ... ah, if you remove the "//" then suddenly it's a
std::vector<int> ... and all compiles ok.

Sorry, disregard that (my newsreader wrapped the code peculiarly).

Anyhow, with the line uncommented it compiles ok for me (gcc 3.4.6, gcc
4.1.2 and Intel icc) - as it should. You don't need a separate definition
for a static integer constant declared (and defined) as above.
 
L

Lionel B

mati said:
Hi

The following code works:

#include <vector>
class C {
private:
static const int m_static = 2;
public:
void f(const std::vector<int>& v)
{
int a = m_static;
std::vector<int> stripped(v.begin()+a, v.end());
//std::vector<int> s2(v.begin()+m_static,v.end());
}
};
int main()
{
C c;
std::vector<int> pv;
int i;
pv.push_back(i);
pv.push_back(i);
pv.push_back(i);
c.f(pv);
}


But when I erase the comment in the void f(...), then compiler gives an
error:

g++ -ansi -Wall -o test test.cpp
/tmp/cckLnGUY.o: In function `C::f(std::vector said:
const&)':
test.cpp:(.text._ZN1C1fERKSt6vectorIiSaIiEE[C::f(std::vector<int,
std::allocator<int> > const&)]+0xdb): undefined reference to
`C::m_static' collect2: ld returned 1 exit status
make: *** [test] Error 1

g++ --version
g++ (GCC) 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)


Can anybody tell me what I'm doing wrong?

Even if you perform the initialization inside the class declaration, you
still need the definition of the static member variable on a cpp file.

I don't believe you do need that for a static integer constant declared
and initialised within a class. I think the OP is either not telling the
whole story or else his/her compiler is badly broken.
 
M

Markus Moll

Hi

Lionel said:
I don't believe you do need that for a static integer constant declared
and initialised within a class. I think the OP is either not telling the
whole story or else his/her compiler is badly broken.

Believe it or not:
(9.4.2 (4)): "If a static data member is of const integral or const
enumeration type, its declaration in the class definition can specify a
constant-initializer which shall be an integral constant expression
(5.19). In that case, the member can appear in integral constant
expressions. 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."

Markus
 
Z

Zeppe

Lionel said:
I don't believe you do need that for a static integer constant declared
and initialised within a class. I think the OP is either not telling the
whole story or else his/her compiler is badly broken.

Usually our knowledge breaks far before the compiler (I'm not being
bumptious, I had to check as well because I thought the code was right
at the beginning).

Check out the Stroustrup at 10.4.6.2 or the standard at 9.4.2#4 for the
rule. Basically, the problem is that the g++ doesn't recognise the
expression "v.begin()+m_static" as a constant expression, and I think
he's right in doing so (but not 100% sure).

Regards,

Zeppe
 
L

Lionel B

Usually our knowledge breaks far before the compiler (I'm not being
bumptious, I had to check as well because I thought the code was right
at the beginning).

I think that's the politest manner I've ever been told I'm talking
rubbish ;)
Check out the Stroustrup at 10.4.6.2 or the standard at 9.4.2#4 for the
rule.

Wow, scary... I use class-initialised static int consts without separate
definitions quite extensively and never had a compiler complain on me...
Basically, the problem is that the g++ doesn't recognise the
expression "v.begin()+m_static" as a constant expression, and I think
he's right in doing so (but not 100% sure).

Strange thing is my gcc 4.1.2 compiles the problem code ok:

g++-4.1.2 --version
g++-4.1.2 (GCC) 4.1.2

whereas the OP's doesn't (perhaps it uses a different stdc++ lib ?).
Might be worth asking on the gcc lists, I guess.
 
Z

Zeppe

Lionel said:
Strange thing is my gcc 4.1.2 compiles the problem code ok:

g++-4.1.2 --version
g++-4.1.2 (GCC) 4.1.2

whereas the OP's doesn't (perhaps it uses a different stdc++ lib ?).
Might be worth asking on the gcc lists, I guess.

Well, that's strange indeed, because it seems that I'm using the same
version as you. Probably the problem relies in the type of v.begin()
that in different architectures has got different definitions. Anyway,
I'm using (g++ -v)

Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v
--enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr
--enable-shared --with-system-zlib --libexecdir=/usr/lib
--without-included-gettext --enable-threads=posix --enable-nls
--program-suffix=-4.1 --enable-__cxa_atexit --enable-clocale=gnu
--enable-libstdcxx-debug --enable-mpfr --enable-checking=release
i486-linux-gnu
Thread model: posix
gcc version 4.1.2 (Ubuntu 4.1.2-0ubuntu4)

and the command line to compile is

g++ -Wall teststatic.cpp -o teststatic

Regards,

Zeppe
 
S

Salt_Peter

Hi




Believe it or not:
(9.4.2 (4)): "If a static data member is of const integral or const
enumeration type, its declaration in the class definition can specify a
constant-initializer which shall be an integral constant expression

The operative word here is class 'definition'.
Not to be confused with the class declaration.
 
R

Ron Natalie

Salt_Peter said:
The operative word here is class 'definition'.
Not to be confused with the class declaration.
Perhaps you are confused. The line in question
is in the class definition.
 
L

Lionel B

Well, that's strange indeed, because it seems that I'm using the same
version as you. Probably the problem relies in the type of v.begin()
that in different architectures has got different definitions. Anyway,
I'm using (g++ -v)

Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v
--enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr
--enable-shared --with-system-zlib --libexecdir=/usr/lib
--without-included-gettext --enable-threads=posix --enable-nls
--program-suffix=-4.1 --enable-__cxa_atexit --enable-clocale=gnu
--enable-libstdcxx-debug --enable-mpfr --enable-checking=release
i486-linux-gnu
Thread model: posix
gcc version 4.1.2 (Ubuntu 4.1.2-0ubuntu4)

Here's mine (never mind the odd paths):

Using built-in specs.
Target: x86_64-unknown-linux-gnu
Configured with: /var/scratch/lionelb/usr/src/gcc-4.1.2/configure --
prefix=/var/scratch/lionelb/opt/gcc-4.1.2 --enable-languages=c,c++ --with-
gnu-as --with-as=/var/scratch/lionelb/opt/binutils-2.17/bin/as --with-gnu-
ld --with-ld=/var/scratch/lionelb/opt/binutils-2.17/bin/ld --enable-
version-specific-runtime-libs --with-build-time-tools=/var/scratch/
lionelb/opt/binutils-2.17/bin
Thread model: posix
gcc version 4.1.2
and the command line to compile is

g++ -Wall teststatic.cpp -o teststatic

Same here.

Compiles ok too with the Intel compiler icc 9.0 and Comeau online.
 
J

Juha Nieminen

Lionel said:
I don't believe you do need that for a static integer constant declared
and initialised within a class. I think the OP is either not telling the
whole story or else his/her compiler is badly broken.

I think that the idea of requiring an actual instance of the const
variable is that some code might want to create a pointer to it.

OTOH, isn't this a problem only if the const variable is public?
If it's private then I can't think of any situation where the
compiler couldn't detect an attempt to create a pointer to it at
compile time. (The problem with a public const variable is that
this public const might be in a library, and some program using
that library might want to create the pointer, in which case a
linker error will happen and it will basically be impossible to
do that. However, with a private const I don't think this could be
possible...)
 
M

mati

mati said:
Hi
(...)
Can anybody tell me what I'm doing wrong?

Thanks for answering, now I know (or at least I think so) everything.
Short: The rhs argument of random iterator's operator+ is const &, and
if there is no namespace scope definition of static const int member,
the linker fails.
 
J

James Kanze

Well, that's strange indeed, because it seems that I'm using the same
version as you. Probably the problem relies in the type of v.begin()
that in different architectures has got different definitions. Anyway,
I'm using (g++ -v)
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v
--enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr
--enable-shared --with-system-zlib --libexecdir=/usr/lib
--without-included-gettext --enable-threads=posix --enable-nls
--program-suffix=-4.1 --enable-__cxa_atexit --enable-clocale=gnu
--enable-libstdcxx-debug --enable-mpfr --enable-checking=release
i486-linux-gnu
Thread model: posix
gcc version 4.1.2 (Ubuntu 4.1.2-0ubuntu4)
and the command line to compile is
g++ -Wall teststatic.cpp -o teststatic

And you get the error, or no?

Formally, not providing the definition is undefined behavior.
Practically, in this particular case, I would not expect there
to be a problem, unless the compiler explicitly does something
special to make it one (so that a program with undefined
behavior doesn't compile). (I wonder if the
"--enable-checking=release" has something to do with it.)

Another issue could be the library. If the address of the
constant is ever taken (including implicitly, e.g. by using it
as an argument for a reference parameter), then it will almost
certainly need a definition. If vector<>::iterator is a class
type (normally the case today, I think, and certainly the case
with g++), and the operator+ takes a reference (which seems
surprising to me, but perhaps there are reasons), and ptrdiff_t
(the type which is added to an iterator) is int, then the
address will be taken. (If ptrdiff_t is not int, then his
static const will first be converted to a temporary ptrdiff_t;
this conversion almost surely doesn't involve taking the
address.)

A quick check in the sources of g++ 4.1.0 show:
__normal_iterator
operator+(const difference_type& __n) const
{ return __normal_iterator(_M_current + __n); }
The operator+ does take a reference, so on machines where
ptrdiff_t (the type of difference_type by default, with the
default allocator) is int, the address of his static const will
be taken, and he must define it. Unless the compiler actually
inlines the function---if he turns on optimization, he might not
need to define it:). (On my machines, ptrdiff_t is almost
always a long, so I wouldn't have to define it either.)

But whatever: the rules say you have to define it, or you have
undefined behavior. And defining it always works. So it's
obvious what you have to do when writing code.
 
Z

Zeppe

James said:
And you get the error, or no?

Yes, I do.
Formally, not providing the definition is undefined behavior.

Well, actually I'm a little bit confused. And, strangely enough, it's
the Stroustrup that confuses me more than the standard. The standard says:

In that case [static member initialized in the class definition], the
member can appear in integral constant expressions within its scope. 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.

Which is clear enough, and it seems to suggest that if you define the
member in the namespace scope you never get wrong. In the Stroustrup,
anyway, it says (very concisely, as usual in the Stroustrup):

If (and only if) you use an initialized member in a way that requires it
to be stored as an object in memory, the member must be (uniquely)
defined somewhere.

I can't understand the "and only if". What does it happen if I define it
anyway? Is there some drawback?
Practically, in this particular case, I would not expect there
to be a problem, unless the compiler explicitly does something
special to make it one (so that a program with undefined
behavior doesn't compile). (I wonder if the
"--enable-checking=release" has something to do with it.)

The problem disappears by enabling some optimizations (-O2). I couldn't
discover which one was the responsible, though, because when I enabled
the optimizations singularly the error was still there.
Another issue could be the library. If the address of the
constant is ever taken (including implicitly, e.g. by using it
as an argument for a reference parameter), then it will almost
certainly need a definition. If vector<>::iterator is a class
type (normally the case today, I think, and certainly the case
with g++), and the operator+ takes a reference (which seems
surprising to me, but perhaps there are reasons), and ptrdiff_t
(the type which is added to an iterator) is int, then the
address will be taken. (If ptrdiff_t is not int, then his
static const will first be converted to a temporary ptrdiff_t;
this conversion almost surely doesn't involve taking the
address.)

I think you are just right, unless some optimization just disables this
by passing by value the const ref, while expanding the inline function
operator+ (at least in the machines in which sizeof(int) == sizeof(int*)).
But whatever: the rules say you have to define it, or you have
undefined behavior. And defining it always works. So it's
obvious what you have to do when writing code.


I agree with you. Just that small thing at the beginning of the post
creates me some doubts...

Regards,

Zeppe
 
J

James Kanze

[...]
Well, actually I'm a little bit confused. And, strangely enough, it's
the Stroustrup that confuses me more than the standard. The standard says:
In that case [static member initialized in the class definition], the
member can appear in integral constant expressions within its scope. 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.
Which is clear enough, and it seems to suggest that if you define the
member in the namespace scope you never get wrong.

More to the point, it says that if you don't the code is wrong,
even if you don't get an immediate error.
In the Stroustrup,
anyway, it says (very concisely, as usual in the Stroustrup):
If (and only if) you use an initialized member in a way that requires it
to be stored as an object in memory, the member must be (uniquely)
defined somewhere.
I can't understand the "and only if". What does it happen if I define it
anyway? Is there some drawback?

Two points:

-- first, Bjarne understands too much about how compilers
work:)---his statement reflects the reality of what
compilers give you, not the words in the standard. and

-- I think you're missing a subtility in his statement: his "if
and only if" applies to "must"; in English, the opposite of
"must" isn't "must not", but "don't have to"; the absolute
requirement is present "if and only if", but it's always
acceptable to declare the variable.

In fact, I would consider this an error in his text, since it
contradicts the standard, even if it is conform with regards to
actual practice. But in the end, it doesn't matter: it's never
wrong to define the variable, it never causes problems, and as
we've just seen, it's not always obvious whether the variable
has been declared in such a way as to require it to be stored in
memory---in the case in question, for example, that actual
function which took the reference was inline, and if the
compiler actually inlined it (which it probably would do if you
demand optimization), the need for the in memory object could
disappear.
 
C

cathode26

Hi

The following code works:

#include <vector>
class C {
private:
static const int m_static = 2;
public:
void f(const std::vector<int>& v)
{
int a = m_static;
std::vector<int> stripped(v.begin()+a, v.end());
//std::vector<int> s2(v.begin()+m_static,v.end());
}
};
int main()
{
C c;
std::vector<int> pv;
int i;
pv.push_back(i);
pv.push_back(i);
pv.push_back(i);
c.f(pv);
}


But when I erase the comment in the void f(...), then compiler gives an
error:

g++ -ansi -Wall -o test test.cpp
/tmp/cckLnGUY.o: In function `C::f(std::vector said:
const&)':
test.cpp:(.text._ZN1C1fERKSt6vectorIiSaIiEE[C::f(std::vector<int,
std::allocator<int> > const&)]+0xdb): undefined reference to `C::m_static'
collect2: ld returned 1 exit status
make: *** [test] Error 1

g++ --version
g++ (GCC) 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)


Can anybody tell me what I'm doing wrong?

So I tried your code in Visual Studio and it works fine, but if you use http://codepad.org/ your code does not compile. It is true, this is a declaration and not a definition. You need to define it like so.

#include <vector>

class C {
private:
static const int m_static = 2;
public:
void f(const std::vector<int>& v);

};

const int C::m_static;

void C::f(const std::vector<int>& v)
{
int a = m_static;
std::vector<int> stripped(v.begin()+a, v.end());
std::vector<int> s2(v.begin()+m_static,v.end());
}

int main()
{
C c;
std::vector<int> pv;
int i;
pv.push_back(i);
pv.push_back(i);
pv.push_back(i);
c.f(pv);
}
 

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

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top