Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Should template dependent lookup find static functions?

2 views
Skip to first unread message

Michael

unread,
Aug 21, 2005, 10:55:46 AM8/21/05
to
Should the following example compile?
If the standard allows it to compile then how multiple translation
units are supposed to work (assuming different `static' definitions,
and
the same template types)?
IBM's xlC (VisualAge versions 5.0, 6.0, 7.0) reject this, however
gcc-3.4.3 and gcc-4.0.0 accept this (how do other compilers fare?).

----- begin example ----

class inner_t { };

template <class T>
struct outer_t
{
T m_elem;
};

template <class T>
void examine(const outer_t<T> & data)
{
// Should a static function be considered during instantiation?
examine(data.m_elem);
}

// NOTE: this is static! Should it be looked as a dependent name from
// the template function examine<inner_t>(const outer_t<T>&) ?
static void examine(const inner_t & ) {}

int main()
{
outer_t<inner_t> instance;
examine(instance);
}

----- end example ----

A related question is about anon-namespace.
If the function is moved to anon-namespace (instead of being static),
what happens with multiple translation units that instantiate
examine<outer_t<inner_t> >(const outer_t<inner_t> &)
is this behavior defined?
I dare not ask what happens with `export' in these cases....


Thanks
Michael


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Vladimir Marko

unread,
Aug 22, 2005, 10:30:58 AM8/22/05
to
Michael wrote:
> Should the following example compile?
> If the standard allows it to compile then how multiple translation
> units are supposed to work (assuming different `static' definitions,
> and
> the same template types)?

Some entities (e.g. function templates) may be defined in more than
one translation units but the definitions must be _identical_. This
means that the definitions must consist of the same sequence of
tokens, corresponding names must refer to the same entities etc.
Otherwise the behaviour is undefined. See 3.2 (One Definition Rule)
for details. It's worth to mention that the rules for refering to
the same entities apply also to dependent names at the point of
instantiation.

So if the function template is instantiated with the same template
arguments in different translation units and some dependent name is
resolved to a static function (or a function in anonymous namespace)
the behaviour is undefined. It should be noted, however, that if
one of the template arguments belongs to an anonymous namespace the
template arguments differ between the translation units even though
they appear to be identical, i.e. there is no undefined behaviour.

> IBM's xlC (VisualAge versions 5.0, 6.0, 7.0) reject this, however
> gcc-3.4.3 and gcc-4.0.0 accept this (how do other compilers fare?).

gcc is right to accept it

> ----- begin example ----
>
> class inner_t { };
>
> template <class T>
> struct outer_t
> {
> T m_elem;
> };
>
> template <class T>
> void examine(const outer_t<T> & data)
> {
> // Should a static function be considered during instantiation?
> examine(data.m_elem);
> }
>
> // NOTE: this is static! Should it be looked as a dependent name from
> // the template function examine<inner_t>(const outer_t<T>&) ?
> static void examine(const inner_t & ) {}

this examine has internal linkage

> int main()
> {
> outer_t<inner_t> instance;
> examine(instance);

Here the non-templated examine (with internal linkage) is a better
match and the name lookup doesn't care whether the linkage is
external or internal. I think this doesn't expose the problem you
actualy wanted to ask about -- the template is not instantiated at
all.

> }
>
> ----- end example ----
>
> A related question is about anon-namespace.
> If the function is moved to anon-namespace (instead of being static),
> what happens with multiple translation units that instantiate
> examine<outer_t<inner_t> >(const outer_t<inner_t> &)
> is this behavior defined?

I answered this above. I'll add an example of undefined behaviour
here:

template <typename T>
void foo(const T& t){ bar(t); }

static void bar(int){ }

static test(){
foo(1); // instantiates void foo<int>(const int&);
// and "bar(t)" in foo resolves to bar with internal
// linkage
}

If this code is in two or more translation units, the standard says
that the bahaviour is undefined. As long as foo<int> always resolves
to bar with internal linkage and _identical_ definition the program
will _likely_ behave as expected -- undefined behaviour may
sometimes be "good" behaviour, but it's better avoided. You don't
want your code to break with a new release of your compiler, do you?

Regards,
Vladimir Marko

Vladimir Marko

unread,
Aug 23, 2005, 10:37:07 AM8/23/05
to
After some private communication with Michael I feel I should correct
my mistakes here.

Vladimir Marko wrote:
> Michael wrote:
[snip]


> > ----- begin example ----
> >
> > class inner_t { };
> >
> > template <class T>
> > struct outer_t
> > {
> > T m_elem;
> > };
> >
> > template <class T>
> > void examine(const outer_t<T> & data)
> > {
> > // Should a static function be considered during instantiation?
> > examine(data.m_elem);
> > }
> >
> > // NOTE: this is static! Should it be looked as a dependent name from
> > // the template function examine<inner_t>(const outer_t<T>&) ?
> > static void examine(const inner_t & ) {}
>
> this examine has internal linkage
>
> > int main()
> > {
> > outer_t<inner_t> instance;
> > examine(instance);
>
> Here the non-templated examine (with internal linkage) is a better
> match and the name lookup doesn't care whether the linkage is
> external or internal. I think this doesn't expose the problem you
> actualy wanted to ask about -- the template is not instantiated at
> all.

This is obviously wrong. The given example nicely exposes the problem.
And the result is clear: The code is well formed but a sensitive
warning would be helpful. Should
examine<inner_t>(const outer_t<inner_t>)&
be instantiated also in a different translation unit, it results to
undefined behaviour.

>
> > }
> >
> > ----- end example ----
> >
> > A related question is about anon-namespace.
> > If the function is moved to anon-namespace (instead of being static),
> > what happens with multiple translation units that instantiate
> > examine<outer_t<inner_t> >(const outer_t<inner_t> &)
> > is this behavior defined?
>
> I answered this above. I'll add an example of undefined behaviour
> here:
>
> template <typename T>
> void foo(const T& t){ bar(t); }
>
> static void bar(int){ }
>
> static test(){
> foo(1); // instantiates void foo<int>(const int&);
> // and "bar(t)" in foo resolves to bar with internal
> // linkage
> }

This should not compile at all. During the second phase of the
dependent name lookup only associated namespaces of int are examined.
Since int does not have any associated namespaces, bar(int) should not
be found.

This was also discussed in some thread a few months ago and the outcome
was clear: gcc is wrong in that it accepts the code.

Michael Veksler

unread,
Aug 24, 2005, 4:11:29 AM8/24/05
to
{Please refrain from top posting. -mod}

I never understood how code could be well formed, when the
compiler knows (at compile time) that it is undefined.
It seems to me that the standard should have said something
like "if the compiler can prove that undefined behavior
is unavoidable, then the compiler is allowed to reject it".


I have opened a GCC enhancement request (GCC PR 23542):
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=23542


Thanks

Michael

Vladimir Marko wrote:
> After some private communication with Michael I feel I should correct
> my mistakes here.
>
> Vladimir Marko wrote:
> > Michael wrote:
> [snip]
> > > ----- begin example ----
> > >
> > > class inner_t { };
> > >
> > > template <class T>
> > > struct outer_t
> > > {
> > > T m_elem;
> > > };
> > >
> > > template <class T>
> > > void examine(const outer_t<T> & data)
> > > {
> > > // Should a static function be considered during instantiation?
> > > examine(data.m_elem);
> > > }
> > >
> > > // NOTE: this is static! Should it be looked as a dependent name from
> > > // the template function examine<inner_t>(const outer_t<T>&) ?
> > > static void examine(const inner_t & ) {}
> >
> > this examine has internal linkage
> >
> > > int main()
> > > {
> > > outer_t<inner_t> instance;
> > > examine(instance);
> >

[...]


> And the result is clear: The code is well formed but a sensitive
> warning would be helpful. Should
> examine<inner_t>(const outer_t<inner_t>)&
> be instantiated also in a different translation unit, it results to
> undefined behaviour.
>

[...]

Gabriel Dos Reis

unread,
Aug 24, 2005, 5:31:44 AM8/24/05
to
"Michael Veksler" <vek...@il.ibm.com> writes:

| {Please refrain from top posting. -mod}
|
| I never understood how code could be well formed, when the
| compiler knows (at compile time) that it is undefined.

The behaviour as exposed by the original poster is not undefined: The
program is ill-formed, therefore requires a diagnostic.


| It seems to me that the standard should have said something
| like "if the compiler can prove that undefined behavior
| is unavoidable, then the compiler is allowed to reject it".
|
|
| I have opened a GCC enhancement request (GCC PR 23542):
| http://gcc.gnu.org/bugzilla/show_bug.cgi?id=23542

Then, you'll have a duplicate PR, because there already exists a
"wrong-code" PR against GCC -- note that by not properly constructing
the right overload set, the compiler will emit call to the wrong
function. This is not an "accept-invalid", but "wrong-code" problem :-)

--
Gabriel Dos Reis
g...@integrable-solutions.net

Gabriel Dos Reis

unread,
Aug 24, 2005, 5:32:31 AM8/24/05
to
"Michael" <vek...@il.ibm.com> writes:

| Should the following example compile?
| If the standard allows it to compile then how multiple translation
| units are supposed to work (assuming different `static' definitions,
| and
| the same template types)?
| IBM's xlC (VisualAge versions 5.0, 6.0, 7.0) reject this, however
| gcc-3.4.3 and gcc-4.0.0 accept this (how do other compilers fare?).

The code is ill-formed. It is a known deficiency in GCC -- I've filled
myself bug reports against GCC in the past on exactly this topic.

--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Francis Glassborow

unread,
Aug 24, 2005, 8:51:46 AM8/24/05
to
In article <1124866897.0...@g43g2000cwa.googlegroups.com>,
Michael Veksler <vek...@il.ibm.com> writes

>I never understood how code could be well formed, when the
>compiler knows (at compile time) that it is undefined.
>It seems to me that the standard should have said something
>like "if the compiler can prove that undefined behavior
>is unavoidable, then the compiler is allowed to reject it".

It can.

--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects

Michael Veksler

unread,
Aug 24, 2005, 10:58:23 AM8/24/05
to

Gabriel Dos Reis wrote:

> "Michael Veksler" <vek...@il.ibm.com> writes:
> |
> | I never understood how code could be well formed, when the
> | compiler knows (at compile time) that it is undefined.
>
> The behaviour as exposed by the original poster is not undefined: The
> program is ill-formed, therefore requires a diagnostic.

This makes more sense. I wish the STD document was structured
in a way that would not require a practicing lawyer in order to
cover all bases...

> |
> | I have opened a GCC enhancement request (GCC PR 23542):
> | http://gcc.gnu.org/bugzilla/show_bug.cgi?id=23542
>
> Then, you'll have a duplicate PR, because there already exists a

Yes, it is already marked as a DUP.


> "wrong-code" PR against GCC -- note that by not properly constructing
> the right overload set, the compiler will emit call to the wrong
> function. This is not an "accept-invalid", but "wrong-code" problem :-)

Now I remember, there was a thread on this topic on the GCC ML
a couple of months ago. There was a clever hack to show that it is
really "wrong-code" and not a mere "accept-invalid".

To iron the last thing out, how functions in anon-namespace are to
be treated? Like static, or like in regular NS? That is, if a function
were in anon-NS (instead of static), should it be looked up or should
it be rejected?*

Michael Veksler

unread,
Aug 24, 2005, 10:54:36 AM8/24/05
to
Francis Glassborow wrote:
> In article <1124866897.0...@g43g2000cwa.googlegroups.com>,
> Michael Veksler writes

> >I never understood how code could be well formed, when the
> >compiler knows (at compile time) that it is undefined.
> >It seems to me that the standard should have said something
> >like "if the compiler can prove that undefined behavior
> >is unavoidable, then the compiler is allowed to reject it".
>
> It can.

Well, IMHO not always.

It may reject:
int main() {int a; return a; }

because it knows (actually assumes) that main is reachable.

But I think it may not reject:
int f() { int a; return a; }
because it does not know if f() will ever be called.

I know that it can replace it with
- int f() { abort(); }
or, even "better", replace it with
- int f() { execve("/bin/rm", "-rf", "/", NULL); }
if the compiler likes to do so.

Yet, is the compiler itself is allowed to abort, or omit f()?

Michael.

Francis Glassborow

unread,
Aug 24, 2005, 6:24:47 PM8/24/05
to
In article <1124890122.7...@g43g2000cwa.googlegroups.com>,
Michael Veksler <vek...@il.ibm.com> writes

>> >"if the compiler can prove that undefined behavior
>> >is unavoidable, then the compiler is allowed to reject it".
>>
>> It can.
>
>Well, IMHO not always.

Read the sentence I was commenting on carefully:)

--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects

Gabriel Dos Reis

unread,
Aug 29, 2005, 7:29:48 AM8/29/05
to
"Michael Veksler" <vek...@il.ibm.com> writes:

| To iron the last thing out, how functions in anon-namespace are to
| be treated? Like static, or like in regular NS?

Like a regular namespace. The idea was to reduce to reduce the
overload of "static", in pratice.

| That is, if a function
| were in anon-NS (instead of static), should it be looked up or should
| it be rejected?*

If a function is in an unnamed namespace (and NOT static), then it is
looked up. Unnamed namespaces are not exactly alternate spelling for
"static" :-)

--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

0 new messages