gcc-4.4.3 failure in rejecting instantiation?

P

Paul Bibbings

I've been working on some code (not mine) that relies on the following
motif leading to a compilation failure, with sensible output, at line
17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:

template<typename T>
struct Error { };

template<typename T>
struct A
{
static const int i = Error<T>::TempArgMustBeInt; // line: 7
};

template<>
struct A<int>
{ };

int main()
{
A<int> A_int;
A<char> A_char; // line: 17
}

I'm thinking that it probably should (or, I have no immediate
understanding of why it wouldn't); whoever wrote the original code
(probably some while ago) thought it should; and, Comeau seems to
agree:

Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 7: error: class "Error<char>" has no member
"TempArgMustBeInt"
static const int i = Error<T>::TempArgMustBeInt;
^
detected during instantiation of class "A<T> [with T=char]" at
line 17
// ...

1 error detected in the compilation of "ComeauTest.c".

What does anyone else think?

Regards

Paul Bibbings
 
A

Alf P. Steinbach /Usenet

* Paul Bibbings, on 20.06.2010 00:15:
I've been working on some code (not mine) that relies on the following
motif leading to a compilation failure, with sensible output, at line
17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:

template<typename T>
struct Error { };

template<typename T>
struct A
{
static const int i = Error<T>::TempArgMustBeInt; // line: 7
};

template<>
struct A<int>
{ };

int main()
{
A<int> A_int;
A<char> A_char; // line: 17
}

I'm thinking that it probably should (or, I have no immediate
understanding of why it wouldn't); whoever wrote the original code
(probably some while ago) thought it should; and, Comeau seems to
agree:

Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 7: error: class "Error<char>" has no member
"TempArgMustBeInt"
static const int i = Error<T>::TempArgMustBeInt;
^
detected during instantiation of class "A<T> [with T=char]" at
line 17
// ...

1 error detected in the compilation of "ComeauTest.c".

What does anyone else think?

Sounds like it incorrectly optimizes the "i" away (perhaps it optimizes A_char
away) before checking whether it exists.

I'd try a typedef instead.

I'm assuming that there is some good reason for the restriction (e.g., A used as
a template template parameter); otherwise just remove the templating... :)


Cheers & hth.,

- Alf
 
P

Paul Bibbings

Alf P. Steinbach /Usenet said:
* Paul Bibbings, on 20.06.2010 00:15:
I've been working on some code (not mine) that relies on the following
motif leading to a compilation failure, with sensible output, at line
17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:

template<typename T>
struct Error { };

template<typename T>
struct A
{
static const int i = Error<T>::TempArgMustBeInt; // line: 7
};

template<>
struct A<int>
{ };

int main()
{
A<int> A_int;
A<char> A_char; // line: 17
}

I'm thinking that it probably should (or, I have no immediate
understanding of why it wouldn't); whoever wrote the original code
(probably some while ago) thought it should; and, Comeau seems to
agree:

Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 7: error: class "Error<char>" has no member
"TempArgMustBeInt"
static const int i = Error<T>::TempArgMustBeInt;
^
detected during instantiation of class "A<T> [with T=char]" at
line 17
// ...

1 error detected in the compilation of "ComeauTest.c".

What does anyone else think?

Sounds like it incorrectly optimizes the "i" away (perhaps it
optimizes A_char away) before checking whether it exists.

I'd try a typedef instead.

I'm assuming that there is some good reason for the restriction (e.g.,
A used as a template template parameter); otherwise just remove the
templating... :)

The code that I was looking at, and which used the above motif, is from
a paper by McNamara and Smaragdakis on "Static interfaces in C++" -
http://www.cc.gatech.edu/~yannis/static-interfaces/static-interfaces.pdf
(I haven't found the date for this paper, but the latest reference is
Alexandrescu, 2000). The example illustrates static dispatch, along the
lines of:

enum {
SET_HASH, // implement using a hash table
SET_BST, // implement using a binary search tree
SET_LL, // implement using a linked list
SET_NONE // no appropriate implementation
};

template<class T>
struct SetDispatch {
static const bool Hash =
StaticIsA<T, Hashtable>::valid;
static const bool LtC =
StaticIsA<T, LessThanComparable<T> >::valid;
static const bool EqT =
StaticIsA<T, EqualityComparable<T> >::valid;
static const int which = Hash ? SET_HASH
: LtC ? SET_BST
: EqT ? SET_LL
: SET_NONE;
};

template<
class T,
int which = SetDispatch said:
struct Set;

template<class T>
struct Set<T, SET_NONE> {
static const int x = Error<T>::
Set_only_works_on_Hashtables_or_LTCs_or_ECs;
};

template<class T>
struct Set<T, SET_LL> { /* ... */ };

template<class T>
struct Set<T, SET_BST> { /* ... */ };

template<class T>
struct Set<T, SET_HASH> { /* ... */ };

By default the implementation does not accept fundamental types as
LessThanComparable, EqualityComparable, etc., and this is achieved with
appropraite specializations of a local traits type:

struct Valid {
static const bool valid = true;
protected:
~Valid() { }
};

template<>
template<>
struct LessThanComparable<int>::Traits<int> : public Valid { };

Without any such specializations, instantiations such as:

Set<char> char_set;

should fail with a nice (4-line) error output finishing with:

error: `Set_only_works_on_Hashtables_or_LTCs_or_ECs' is not a member
of `Error<char>'

with the advantage that the failure:

1. signals what is `missing' for the particular type as template
argument;
2. signals the type that failed as template argument (the reason for
parameterizing Error); and
3. gives concise error output that doesn't go deep into the bowels of
the implementation.

However, *without* the traits specialization for char, the
implementation *allows*:

Set<char> char_set;

To get the code working I can replace:

template<class T>
struct Set<T, SET_NONE> {
static const int x = Error<T>::
Set_only_works_on_Hashtables_or_LTCs_or_ECs;
};

with:

template<class T>
struct Set<T, SET_NONE> {
Set() {
const int x = Error<T>::
Set_only_works_on_Hashtables_or_LTCs_or_ECs;
}
};

but the effect is not necessarily the same in all cases (or, at least, I
would have to think it through to decide whether it is).

Regards

Paul Bibbings
 
P

Paul Bibbings

Paavo Helde said:
I've been working on some code (not mine) that relies on the following
motif leading to a compilation failure, with sensible output, at line
17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:

template<typename T>
struct Error { };

template<typename T>
struct A
{
static const int i = Error<T>::TempArgMustBeInt; // line: 7
};

template<>
struct A<int>
{ };

int main()
{
A<int> A_int;
A<char> A_char; // line: 17
}

I'm thinking that it probably should (or, I have no immediate
understanding of why it wouldn't); whoever wrote the original code
(probably some while ago) thought it should; and, Comeau seems to
agree:

Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 7: error: class "Error<char>" has no member
"TempArgMustBeInt"
static const int i = Error<T>::TempArgMustBeInt;
^
detected during instantiation of class "A<T> [with T=char]" at
line 17
// ...

1 error detected in the compilation of "ComeauTest.c".

What does anyone else think?

This boils down to the question if static data members of a template
class will be instantiated and initialized if they are never used.

The 2003 standard says in 14.7.1:"... in particular, the initialization
(and any associated side-effects) of a static data member does not occur
unless the static data member is itself used in a way that requires the
definition of the static data member to exist."

This would suggest that gcc 4.4 is right and Comeau wrong, but this would
be a bit surprising. Probably I have overlooked something.

The fix for the code to get it working (i.e. failing ;-) as intended in
gcc 4.4 would be easy though, the static data member should be accessed
somehow, triggering its instantiation (and a cleanly compiling variant of
it to be added to A<int>).

So, one fix (not necessarily the best) might be to augment the primary
template for struct A (above) as?:

template<typename T>
struct A
{
A() { &A::i; }
static const int i = Error<T>::TempArgMustBeInt;
};

As to your reference to [temp.inst] §14.7.1/1, I can't immediately see
that you have overlooked anything, and I would agree with your
(tentative) assessment of gcc being "right". (The code that I have
extracted the example from occurs in a paper written, certainly, after
2000, and I note that the cited subclause did not change from C++98 to
C++03, although compilers might have implemented it incorrectly at the
time.)

As to Comeau being wrong, however, I don't think it's as straightforward
to say that. As stated on the `tryitout' start page, Comeau
instantiates templates at link time and, of course, the online compiler
doesn't link. This is the reason for the online compiler defaulting to
the -tused option to force some instantiations, and this is how the
above code was given to Comeau. But not knowing how the online compiler
handles compilation that requires instantiation even without the -tused
flag, it is hard to comment on the fact that it *still* fails even with
-tused turned off.

Does anyone here perhaps have a local installation of Comeau that they
could try the above code with - compile and link?

Regards

Paul Bibbings
 
P

Paul Bibbings

Paul Bibbings said:
The code that I was looking at, and which used the above motif, is from
a paper by McNamara and Smaragdakis on "Static interfaces in C++" -
http://www.cc.gatech.edu/~yannis/static-interfaces/static-interfaces.pdf
(I haven't found the date for this paper, but the latest reference is
Alexandrescu, 2000).

Brian McNamara and Yannis Smaragdakis, ¡°Static Interfaces in C++¡±, in
the C++ Template Programming Workshop, Erfurt, Germany, October
2000.

Regards

Paul Bibbings
 
A

Alf P. Steinbach /Usenet

* Paul Bibbings, on 20.06.2010 12:04:
Alf P. Steinbach /Usenet said:
* Paul Bibbings, on 20.06.2010 00:15:
I've been working on some code (not mine) that relies on the following
motif leading to a compilation failure, with sensible output, at line
17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:

template<typename T>
struct Error { };

template<typename T>
struct A
{
static const int i = Error<T>::TempArgMustBeInt; // line: 7
};

template<>
struct A<int>
{ };

int main()
{
A<int> A_int;
A<char> A_char; // line: 17
}

I'm thinking that it probably should (or, I have no immediate
understanding of why it wouldn't); whoever wrote the original code
(probably some while ago) thought it should; and, Comeau seems to
agree:

Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 7: error: class "Error<char>" has no member
"TempArgMustBeInt"
static const int i = Error<T>::TempArgMustBeInt;
^
detected during instantiation of class "A<T> [with T=char]" at
line 17
// ...

1 error detected in the compilation of "ComeauTest.c".

What does anyone else think?

Sounds like it incorrectly optimizes the "i" away (perhaps it
optimizes A_char away) before checking whether it exists.

I'd try a typedef instead.

I'm assuming that there is some good reason for the restriction (e.g.,
A used as a template template parameter); otherwise just remove the
templating... :)

The code that I was looking at, and which used the above motif, is from
a paper by McNamara and Smaragdakis on "Static interfaces in C++" -
http://www.cc.gatech.edu/~yannis/static-interfaces/static-interfaces.pdf
(I haven't found the date for this paper, but the latest reference is
Alexandrescu, 2000). The example illustrates static dispatch, along the
lines of:

enum {
SET_HASH, // implement using a hash table
SET_BST, // implement using a binary search tree
SET_LL, // implement using a linked list
SET_NONE // no appropriate implementation
};

template<class T>
struct SetDispatch {
static const bool Hash =
StaticIsA<T, Hashtable>::valid;
static const bool LtC =
StaticIsA<T, LessThanComparable<T> >::valid;
static const bool EqT =
StaticIsA<T, EqualityComparable<T> >::valid;
static const int which = Hash ? SET_HASH
: LtC ? SET_BST
: EqT ? SET_LL
: SET_NONE;
};

template<
class T,
struct Set;

template<class T>
struct Set<T, SET_NONE> {
static const int x = Error<T>::
Set_only_works_on_Hashtables_or_LTCs_or_ECs;

Try replacing the constant with

typedef typename Error<T>::Set_only_works_on_what_it_works_on Oops;

Disclaimer: I haven't tried this.

But I can imagine that it would work.


Cheers & hth.,

- Alf
 
A

Alf P. Steinbach /Usenet

* Paul Bibbings, on 20.06.2010 12:36:
Paavo Helde said:
I've been working on some code (not mine) that relies on the following
motif leading to a compilation failure, with sensible output, at line
17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:

template<typename T>
struct Error { };

template<typename T>
struct A
{
static const int i = Error<T>::TempArgMustBeInt; // line: 7
};

template<>
struct A<int>
{ };

int main()
{
A<int> A_int;
A<char> A_char; // line: 17
}

I'm thinking that it probably should (or, I have no immediate
understanding of why it wouldn't); whoever wrote the original code
(probably some while ago) thought it should; and, Comeau seems to
agree:

Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 7: error: class "Error<char>" has no member
"TempArgMustBeInt"
static const int i = Error<T>::TempArgMustBeInt;
^
detected during instantiation of class "A<T> [with T=char]" at
line 17
// ...

1 error detected in the compilation of "ComeauTest.c".

What does anyone else think?

This boils down to the question if static data members of a template
class will be instantiated and initialized if they are never used.

The 2003 standard says in 14.7.1:"... in particular, the initialization
(and any associated side-effects) of a static data member does not occur
unless the static data member is itself used in a way that requires the
definition of the static data member to exist."

This would suggest that gcc 4.4 is right and Comeau wrong, but this would
be a bit surprising. Probably I have overlooked something.
[snip]

As to your reference to [temp.inst] §14.7.1/1, I can't immediately see
that you have overlooked anything, and I would agree with your
(tentative) assessment of gcc being "right".

Consider the following program (off the cuff):

struct S
{
static int const v = 42;
};

#include <iostream>
int main()
{ std::cout << S::v << std::endl; }

Here the definition of S::v is not required to exist.

And in fact there is no definition of S::v, just a pure declaration. :)

It may look as if there is a definition of S::v, but it really is just a pure
declaration.

A definition would be, at namespace scope,

int const S::v;

which might look like a pure declaration, but is really a definition.


Cheers,

- Alf
 
Ö

Öö Tiib

I've been working on some code (not mine) that relies on the following
motif leading to a compilation failure, with sensible output, at line
17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:

   template<typename T>
   struct Error { };

   template<typename T>
   struct A
   {
      static const int i = Error<T>::TempArgMustBeInt;  // line: 7
   };

   template<>
   struct A<int>
   { };

   int main()
   {
      A<int>  A_int;
      A<char> A_char;                                   // line: 17
   }

I'm thinking that it probably should (or, I have no immediate
understanding of why it wouldn't); whoever wrote the original code
(probably some while ago) thought it should; and, Comeau seems to
agree:

   Comeau C/C++ 4.3.10.1 (Oct  6 2008 11:28:09) for
   ONLINE_EVALUATION_BETA2
   Copyright 1988-2008 Comeau Computing.  All rights reserved.
   MODE:strict errors C++ noC++0x_extensions

   "ComeauTest.c", line 7: error: class "Error<char>" has no member
     "TempArgMustBeInt"
     static const int i = Error<T>::TempArgMustBeInt;
                                    ^
          detected during instantiation of class "A<T> [with T=char]" at
          line 17
   // ...

   1 error detected in the compilation of "ComeauTest.c".

What does anyone else think?

What we have? For bare human eye it looks wrong. Comeau issues a
diagnostic and gcc does not issue one.

It is probably one of the "program is ill-formed but no diagnostic is
required" cases or undefined by standard cases. I can not imagine that
standard is saying somewhere it is well-formed program.

The feature of declaring static integral constant values in class
declaration is sort of exceptional (so something about it might be is
left undefined). Standard talking about templates contains "no
diagnostic required" rather often so it may be one from there.

As for 2000, people were trying every sort of possibilities back then
how to use templates and macros for meta-programming. Resulting
compiler messages were terrible to read so people also investigated
how to improve compile-time error checking. Casual black magic in a
book is perhaps not that uncommon because some tricks used and also
published were found by trial and error.
 
P

Paul Bibbings

Alf P. Steinbach /Usenet said:
* Paul Bibbings, on 20.06.2010 12:04:
Alf P. Steinbach /Usenet said:
* Paul Bibbings, on 20.06.2010 00:15:

I've been working on some code (not mine) that relies on the following
motif leading to a compilation failure, with sensible output, at line
17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:

template<typename T>
struct Error { };

template<typename T>
struct A
{
static const int i = Error<T>::TempArgMustBeInt; // line: 7
};

template<>
struct A<int>
{ };

int main()
{
A<int> A_int;
A<char> A_char; // line: 17
}

I'm thinking that it probably should (or, I have no immediate
understanding of why it wouldn't); whoever wrote the original code
(probably some while ago) thought it should; and, Comeau seems to
agree:

Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 7: error: class "Error<char>" has no member
"TempArgMustBeInt"
static const int i = Error<T>::TempArgMustBeInt;
^
detected during instantiation of class "A<T> [with T=char]" at
line 17
// ...

1 error detected in the compilation of "ComeauTest.c".

What does anyone else think?

Sounds like it incorrectly optimizes the "i" away (perhaps it
optimizes A_char away) before checking whether it exists.

I'd try a typedef instead.

I'm assuming that there is some good reason for the restriction (e.g.,
A used as a template template parameter); otherwise just remove the
templating... :)

The code that I was looking at, and which used the above motif, is from
a paper by McNamara and Smaragdakis on "Static interfaces in C++" -
http://www.cc.gatech.edu/~yannis/static-interfaces/static-interfaces.pdf
(I haven't found the date for this paper, but the latest reference is
Alexandrescu, 2000). The example illustrates static dispatch, along the
lines of:

enum {
SET_HASH, // implement using a hash table
SET_BST, // implement using a binary search tree
SET_LL, // implement using a linked list
SET_NONE // no appropriate implementation
};

template<class T>
struct SetDispatch {
static const bool Hash =
StaticIsA<T, Hashtable>::valid;
static const bool LtC =
StaticIsA<T, LessThanComparable<T> >::valid;
static const bool EqT =
StaticIsA<T, EqualityComparable<T> >::valid;
static const int which = Hash ? SET_HASH
: LtC ? SET_BST
: EqT ? SET_LL
: SET_NONE;
};

template<
class T,
struct Set;

template<class T>
struct Set<T, SET_NONE> {
static const int x = Error<T>::
Set_only_works_on_Hashtables_or_LTCs_or_ECs;

Try replacing the constant with

typedef typename Error<T>::Set_only_works_on_what_it_works_on Oops;

Disclaimer: I haven't tried this.

But I can imagine that it would work.

To go back to the original example code (top), if I replace the primary
template with:

template<typename T>
struct A
{
typedef typename Error<T>::TempArgMustBeInt type;
};

then everything works nicely and, of course, this is the `conventional'
(modern) way of doing this kind of thing.

14:42:48 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/ $gcc -c test.cpp
test_.cpp: In instantiation of ¡®A<char>¡¯:
test.cpp:19: instantiated from here
test.cpp:9: error: no type named ¡®TempArgMustBeInt¡¯ in ¡®struct
Error<char>¡¯

The same method can, then - as you say - be applied in (adapted to) the
code from McNamara and Smaragdakis, I'm sure.

Regards

Paul Bibbings
 
P

Paul Bibbings

Alf P. Steinbach /Usenet said:
Consider the following program (off the cuff):

struct S
{
static int const v = 42;
};

#include <iostream>
int main()
{ std::cout << S::v << std::endl; }

Here the definition of S::v is not required to exist.

And in fact there is no definition of S::v, just a pure declaration. :)

It may look as if there is a definition of S::v, but it really is just
a pure declaration.

A definition would be, at namespace scope,

int const S::v;

which might look like a pure declaration, but is really a definition.

The reference for this is [class.static.data] §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."

But, from my reading of this subclause, I don't feel certain that the
above example - which gcc is happy with - is correct.

The reason for my doubt stems from [basic.def.odr] §3.2/2:

"An expression is /potentially evaluated/ unless it appears where an
integral constant expression is required... An object or
non-overloaded function is /used/ if its name appears in a
potentially-evaluated expression. ..."

What this means, in effect - if I am reading it correctly - is that
"appear[ance] where an integral constant expression is required" doesn't
constitute a "use." Therefore the last sentence of §9.4.2/4 doesn't
apply in this case, and the following is valid, IIUC:

struct S
{
static const int v = 42;
static const int w = v - 1; // #1
}

In this case, in line #1, v "appears where an integral constant
expression is required" and thus a definition (according to §9.4.2/4) is
*not* required.

However, since (from your code), the call:

std::cout << S::v << std::endl;

invokes:

std::basic_ostream<char, std::char_traits<char> >&
std::eek:stream::eek:perator<<(int);

I can't see that we have the same situation here. Matching the int
parameter to the call to std::eek:stream::eek:p<< does *not*, in my view,
constitute a context "where an integral constant expression is
required." This seems clear from the fact that:

int i = 42;
std::cout << i << std::endl; // #2

works, where:

template<int N> // requires const. integral expr. as argument
struct A { };
A<i> a;

does not. Therefore, the expression `i' (in #2) seems to constitute a
`use' according to §3.2/2 and thereby invokes "The member shall still
be defined in a namespace scope if it is *used* in the program" from
§9.4.2/4. [my emphasis]

Regards

Paul Bibbings
 
A

Alf P. Steinbach /Usenet

* Paul Bibbings, on 20.06.2010 16:44:
Alf P. Steinbach /Usenet said:
Consider the following program (off the cuff):

struct S
{
static int const v = 42;
};

#include<iostream>
int main()
{ std::cout<< S::v<< std::endl; }

Here the definition of S::v is not required to exist.

And in fact there is no definition of S::v, just a pure declaration. :)

It may look as if there is a definition of S::v, but it really is just
a pure declaration.

A definition would be, at namespace scope,

int const S::v;

which might look like a pure declaration, but is really a definition.

The reference for this is [class.static.data] §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."

But, from my reading of this subclause, I don't feel certain that the
above example - which gcc is happy with - is correct.

The reason for my doubt stems from [basic.def.odr] §3.2/2:

"An expression is /potentially evaluated/ unless it appears where an
integral constant expression is required... An object or
non-overloaded function is /used/ if its name appears in a
potentially-evaluated expression. ..."

What this means, in effect - if I am reading it correctly - is that
"appear[ance] where an integral constant expression is required" doesn't
constitute a "use." Therefore the last sentence of §9.4.2/4 doesn't
apply in this case, and the following is valid, IIUC:

struct S
{
static const int v = 42;
static const int w = v - 1; // #1
}

In this case, in line #1, v "appears where an integral constant
expression is required" and thus a definition (according to §9.4.2/4) is
*not* required.

However, since (from your code), the call:

std::cout<< S::v<< std::endl;

invokes:

std::basic_ostream<char, std::char_traits<char> >&
std::eek:stream::eek:perator<<(int);

I can't see that we have the same situation here. Matching the int
parameter to the call to std::eek:stream::eek:p<< does *not*, in my view,
constitute a context "where an integral constant expression is
required." This seems clear from the fact that:

int i = 42;
std::cout<< i<< std::endl; // #2

works, where:

template<int N> // requires const. integral expr. as argument
struct A { };
A<i> a;

does not. Therefore, the expression `i' (in #2) seems to constitute a
`use' according to §3.2/2 and thereby invokes "The member shall still
be defined in a namespace scope if it is *used* in the program" from
§9.4.2/4. [my emphasis]

Regarding the formal you're right, AFAICS.

However, that's only in the formal, and only because the committee goofed. :)

The original issue was raised by Bill Gibbons in

http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_closed.html#82

where in particular he mentions

"Use as an integral constant expression. Only the value is used. A static
data member with its initializer given in the class need not have a
namespace-scope definition."

The committee IMHO incorrectly decided that it was a duplicate of an earlier
issue raised by Bill, effectively that the main point of #83 was not a
"subtantive part" of the issue, and tackled instead Bill's earlier

http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#48

by resolving it to the text you quoted.

I think it might be a good idea to post this to [comp.std.c++].

It's really bad when /formally/ a compile time constant can't be used as such; I
can't imagine that that was the intent (as evident from the quote above it was
certainly not Bill's intent). In the above program S::v is used as integral
constant expression. The problem wrt. to the formal is that for this context an
integral constant expression is not "required", it's just permitted...


Cheers,

- Alf
 
P

Paul Bibbings

Alf P. Steinbach /Usenet said:
* Paul Bibbings, on 20.06.2010 16:44:
Alf P. Steinbach /Usenet said:
Consider the following program (off the cuff):

struct S
{
static int const v = 42;
};

#include<iostream>
int main()
{ std::cout<< S::v<< std::endl; }

Here the definition of S::v is not required to exist.

And in fact there is no definition of S::v, just a pure declaration. :)

It may look as if there is a definition of S::v, but it really is just
a pure declaration.

A definition would be, at namespace scope,

int const S::v;

which might look like a pure declaration, but is really a definition.

The reference for this is [class.static.data] §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."

But, from my reading of this subclause, I don't feel certain that the
above example - which gcc is happy with - is correct.

The reason for my doubt stems from [basic.def.odr] §3.2/2:

"An expression is /potentially evaluated/ unless it appears where an
integral constant expression is required... An object or
non-overloaded function is /used/ if its name appears in a
potentially-evaluated expression. ..."

What this means, in effect - if I am reading it correctly - is that
"appear[ance] where an integral constant expression is required" doesn't
constitute a "use." Therefore the last sentence of §9.4.2/4 doesn't
apply in this case, and the following is valid, IIUC:

struct S
{
static const int v = 42;
static const int w = v - 1; // #1
}

In this case, in line #1, v "appears where an integral constant
expression is required" and thus a definition (according to §9.4.2/4) is
*not* required.

However, since (from your code), the call:

std::cout<< S::v<< std::endl;

invokes:

std::basic_ostream<char, std::char_traits<char> >&
std::eek:stream::eek:perator<<(int);

I can't see that we have the same situation here. Matching the int
parameter to the call to std::eek:stream::eek:p<< does *not*, in my view,
constitute a context "where an integral constant expression is
required." This seems clear from the fact that:

int i = 42;
std::cout<< i<< std::endl; // #2

works, where:

template<int N> // requires const. integral expr. as argument
struct A { };
A<i> a;

does not. Therefore, the expression `i' (in #2) seems to constitute a
`use' according to §3.2/2 and thereby invokes "The member shall still
be defined in a namespace scope if it is *used* in the program" from
§9.4.2/4. [my emphasis]

Regarding the formal you're right, AFAICS.

However, that's only in the formal, and only because the committee goofed. :)

The original issue was raised by Bill Gibbons in

http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_closed.html#82

where in particular he mentions

"Use as an integral constant expression. Only the value is used. A static
data member with its initializer given in the class need not have a
namespace-scope definition."

The committee IMHO incorrectly decided that it was a duplicate of an
earlier issue raised by Bill, effectively that the main point of #83
was not a "subtantive part" of the issue, and tackled instead Bill's
earlier

http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#48

by resolving it to the text you quoted.

I think it might be a good idea to post this to [comp.std.c++].

It's really bad when /formally/ a compile time constant can't be used
as such; I can't imagine that that was the intent (as evident from the
quote above it was certainly not Bill's intent). In the above program
S::v is used as integral constant expression. The problem wrt. to the
formal is that for this context an integral constant expression is not
"required", it's just permitted...

That's interesting and, following through your references, I would agree
with your assessment of what happened; that, in resolving the earlier
issue, a key part of Gibbon's later DR was missed.

I see that the wording has changed in the FCD with the current (C++03):

C++03: [basic.def.odr] §3.2/2
"An object or non-overloaded function is /used/ if its name appears
in a potentially-evaluated expression."

where (ibid.):

"An expression is /potentially evaluated/ unless it appears where an
integral constant expression is required (see 5.19),..."

becoming:
C++0x FCD: [basic.def.odr] $3.2/2
"A variable or non-overloaded function whose name appears as a
potentially-evaluated expression is /used/ unless it is an object
that satisfies the requirements for appearing in a constant
expression (5.19) and the lvalue-to-rvalue conversion (4.1) is
immediately applied."

where (ibid.):

"An expression is /potentially evaluated/ unless it is an unevaluated
operand (Clause 5) or a subexpression thereof."

I think that the changes are significant and, in the face of the
introduction of constexpr, §5.19 [expr.const] is greatly changed also.
I haven't followed through all the details in relation to the formal
correctness (or not) of the above code under C++0x but what jumps out at
me in particular are:

1. The removal of "unless it appears where an integral constant
expression is required" from the definition of
`potentially-evaluated'; followed by
2. Its reappearance in the definition of `used' where it is reworded
as "unless it is an object that "satisfies the requirements for
appearing in a constant expression."

I am wondering, then, if the specific change from "appears where an
integral constant expression is required" to "satisfies the requirements
for appearing in a constant expression" makes all the difference for the
code under consideration. Clearly, S::v *does* satisfy the requirements
for appearing in a constant expression, etc. and so, to my reading, its
appearance in:

std::cout << S::v << std::endl;

doesn't constitute a use in the new sense and so the last sentence of
(the unchanged) §9.4.2/4 doesn't apply - *no* definition required.

Regards

Paul Bibbings
 
J

James Kanze

I've been working on some code (not mine) that relies on the
following motif leading to a compilation failure, with
sensible output, at line 17 (below). The trouble is, with
gcc-4.4.3, it *doesn't* fail:
template<typename T>
struct Error { };
template<typename T>
struct A
{
static const int i = Error<T>::TempArgMustBeInt; // line: 7
};
template<>
struct A<int>
{ };
int main()
{
A<int> A_int;
A<char> A_char; // line: 17
}
I'm thinking that it probably should (or, I have no immediate
understanding of why it wouldn't); whoever wrote the original code
(probably some while ago) thought it should; and, Comeau seems to
agree:
Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions
"ComeauTest.c", line 7: error: class "Error<char>" has no member
"TempArgMustBeInt"
static const int i = Error<T>::TempArgMustBeInt;
^
detected during instantiation of class "A<T> [with T=char]" at
line 17
// ...
1 error detected in the compilation of "ComeauTest.c".
What does anyone else think?

I think the standard is a bit vague about this. According to
the standard, a static data member is only instantiated if it is
used. (See §14.7.1.) I don't see anything in your code which
would require an instance of A::i, so the definition of A::i
should not be instantiated. Which is good, because you don't
provide a definition (in the sense of the standard). On the
other hand, this is the only case I know in the standard where
a declaration which is not a definition can contain an
initializer---is the initializer considered part of the
declaration, or a pre-definition, of sorts? In the first case,
it should be instantiated (triggering the error); in the second,
it shouldn't be instantiated unless i is actually used.

I think the classical way of doing this is something like:

template <typename T>
struct A
{
enum { error = sizeof(Error<T>::whatever) };
};

The enum constant must be instantiated any time the class is.
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top