Difference between inline initialization and definition.

G

Gianni Mariani

I posted a gcc bug

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=13332


here is the text:
-----------------------------------------------------------

This code will compile fine but fail on a linker error.

templ_examp.cpp:41: undefined reference to `Bounds<float,
(unsigned)4>::size'

However if "static const unsigned size" is changed from an "unsigned" to an
"int" or "short", it appears to work correctly.


template <typename T, unsigned N>
class Bounds
{
public:

static const unsigned size = N;
T * ptr;

T & operator[] ( unsigned i )
{
if ( i >= size )
{
throw "Array outa bounds";
}

return ptr[ i ];
}

Bounds( T ( & i_ptr)[N] )
: ptr( i_ptr )
{
}
};

template <typename T, unsigned N>
Bounds<T,N> make_bounds( T ( & i_ptr)[N] )
{
return Bounds<T,N>( i_ptr );
}


float array[] = { 1., 2., 3., 4. };

#include <iostream>

int main()
{

std::cout << make_bounds( array )[ 2 ];
std::cout << make_bounds( array ).size;
// std::cout << make_bounds( array )[ 20 ]; // throw exception
}


------- Additional Comment #1 From Andrew Pinski 2003-12-06 08:18
[reply] -------

From bug 13259 which this is bug is a dup of:
The standard says that you may inline-initialize the static variable,
but you still need to _define_ it somewhere so that a memory location
is assigned to it.

-----------------------------------------------------------

if below is not a definition:

....
static const unsigned size = N;
....

what is it ?
 
R

Rob Williscroft

Gianni Mariani wrote in
I posted a gcc bug

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=13332


here is the text:
-----------------------------------------------------------

This code will compile fine but fail on a linker error.

templ_examp.cpp:41: undefined reference to `Bounds<float,
(unsigned)4>::size'

However if "static const unsigned size" is changed from an "unsigned"
to an "int" or "short", it appears to work correctly.

Actually it works inspite of the fact your code is incorrect. I can't
remember wether a compiler is required to produce a diagnostic (an
error / warning meassage) when this rule is violated, but I think not.
template <typename T, unsigned N>
class Bounds
{
public:

static const unsigned size = N;
T * ptr;

T & operator[] ( unsigned i )
{

This usage of 'size' is not one where a compile-time constant is
required. (see below).

Note that when you change size to int or short, the comparison below
requires a conversion from (int or short) to unsigned. It would appear
that gcc "thinks" its easier to do this by converting the initializer (N)
directly (hence no linking). When size is unsigned gcc "thinks" a memory
reference would be better, so the linker discovers your incorrect code.

if ( i >= size )
{
throw "Array outa bounds";
}

return ptr[ i ];
}

Bounds( T ( & i_ptr)[N] )
: ptr( i_ptr )
{
}
};

template <typename T, unsigned N>
Bounds<T,N> make_bounds( T ( & i_ptr)[N] )
{
return Bounds<T,N>( i_ptr );
}


float array[] = { 1., 2., 3., 4. };

#include <iostream>

int main()
{

std::cout << make_bounds( array )[ 2 ];
std::cout << make_bounds( array ).size;
// std::cout << make_bounds( array )[ 20 ]; // throw exception
}


------- Additional Comment #1 From Andrew Pinski 2003-12-06 08:18
[reply] -------

From bug 13259 which this is bug is a dup of:
The standard says that you may inline-initialize the static variable,
but you still need to _define_ it somewhere so that a memory location
is assigned to it.

In other words it isn't a bug (in gcc).
-----------------------------------------------------------

if below is not a definition:

...
static const unsigned size = N;
...

what is it ?

Its a declaration and an intialization, but it is not a *definition*.

The value of this initialization can only be used where a compile-time
contexts is *required*, i.e. as an array bounds, as a template paramiter
etc. Otherwise a defenition is required.

The defenition should be like this:

template <typename T, unsigned N>
unsigned const Bounds< T, N >::size; /* no initializer */

Rob.
 
G

Gianni Mariani

Rob said:
Gianni Mariani wrote in news:[email protected]:
Its a declaration and an intialization, but it is not a *definition*.

The value of this initialization can only be used where a compile-time
contexts is *required*, i.e. as an array bounds, as a template paramiter
etc. Otherwise a defenition is required.

The defenition should be like this:

template <typename T, unsigned N>
unsigned const Bounds< T, N >::size; /* no initializer */

OK, I get it. Thanks for clearing that up.

However, I still don't get the reason for this. Is there a good reason
to be splitting hairs like this ? After all, the compiler can certainly
work out when a reference is needed or not. If it was uninitiialized, I
can imagine why one should have a declaration, but is seems very strange
to be able to initialize a variable an not declare it.

With C++, you learn somthing every day :)
 
R

Rob Williscroft

Gianni Mariani wrote in
OK, I get it. Thanks for clearing that up.

However, I still don't get the reason for this. Is there a good
reason to be splitting hairs like this ?

Not that I'm aware off, particularly as templates get "merged" all the
time.

The /bad/ reason is probably the comittee though 1 exception for
static integral constants was enough.
After all, the compiler can
certainly work out when a reference is needed or not. If it was
uninitiialized, I can imagine why one should have a declaration, but
is seems very strange to be able to initialize a variable an not
declare it.

With C++, you learn somthing every day :)

Yup.

I thought of this workaround (assuming there's some good reason
for avoiding the *definition*), since you don't need a definition
if you avoid all non-compile-time contexts.

template < unsigned N > class whatever
{
static unsigned const size = N;
....
....
if (i < sizeof( char[ size ] )) ...

or:

....
enum {Size = size};
if (i < Size) ...


Maybe a member function would be better though:

static unsigned runtime_size()
{
return unsigned( sizeof( char[ size ] ));
}

Rob. -- http://www.victim-prime.dsl.pipex.com/
 
G

Gianni Mariani

Rob said:
Gianni Mariani wrote in news:[email protected]:
I thought of this workaround (assuming there's some good reason
for avoiding the *definition*), since you don't need a definition
if you avoid all non-compile-time contexts.

template < unsigned N > class whatever
{
static unsigned const size = N;
...
...
if (i < sizeof( char[ size ] )) ...

or:

...
enum {Size = size};
if (i < Size) ...


Maybe a member function would be better though:

static unsigned runtime_size()
{
return unsigned( sizeof( char[ size ] ));
}

This is cool ...

In this particular case though you could more simply write "return N".

I'm not too concerned about having to add a declaration, I'm more
concerned about the apparent violation of the "rule of least astonishment".
 

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

Staff online

Members online

Forum statistics

Threads
473,767
Messages
2,569,571
Members
45,045
Latest member
DRCM

Latest Threads

Top