The actual result of name resolution in the class template is different from the c++ 03 standard
Asked Answered
C

3

14

I test the code in the c++ standard ISO/IEC 14882-03 14.6.1/9 on Xcode 4.1 and Visual Studio 2008. The outputs of the two compiler are both different from the expected result of the standard.

The code is pasted below.

#include <stdio.h>
#include <iostream>
using namespace std;

void f(char);

template <class T > void g(T t)
{
    f(1);
    f(T(1));
    f(t);
}

void f(int);
void h()
{
    g(2);
    g('a');
}

void f(int)
{
     cout << "f int" << endl;
}


void f(char)
{
    cout << "f char" << endl;
}


int main() { 
    h();  
    return 0;
}

As the description of the standard. The expected output should be

f char
f int
f int
f char
f char
f char

Build and run the code on Xcode 4.1. The output is as below. In the build settings, I tried to change the "Compiler for C/C++/Object-C" to be Apple LLVM Compiler 2.1, Gcc 4.2 and LLVM GCC 4.2. The outputs are the same.

f char
f char
f char
f char
f char
f char

Build and run the code on Microsoft Visual Studio 2008. The output is as below.

f int
f int
f int
f int
f char
f char

The description (14.6.1/9) of the standard is pasted below.

If a name does not depend on a template-parameter (as defined in 14.6.2), a declaration (or set of declara- tions) for that name shall be in scope at the point where the name appears in the template definition; the name is bound to the declaration (or declarations) found at that point and this binding is not affected by declarations that are visible at the point of instantiation. [Example:

void f(char);
template<class T> void g(T t)
{
f(1); // f(char) 
f(T(1)); // dependent 
f(t); // dependent 
dd++; // not dependent
}
void f(int);
double dd;
void h()
{
// error: declaration for dd not found
g(2); // will cause one call of f(char) followed // by two calls of f(int)
g(’a’); // will cause three calls of f(char) 

—end example]

The code is well-formed to the compilers, but the outputs are different. It would be very dangerous to port this code to different platforms.

Does somebody have the background why these compilers don't follow the standard?

Edit on 10/11/2011

Per http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#197, the example in the standard is wrong. I test the code below on Clang and Gcc.

#include <stdio.h>
#include <iostream>
using namespace std;

void f(char);

template <class T > void g(T t)
{
    f(1);
    f(T(1));
    f(t);
}

enum E{ e };

void f(E );
void h()
{
    g(e);
    g('a');
}

void f(E )
{
    cout << "f E" << endl;
}

void f(char)
{
    cout << "f char" << endl;
}

int main() { 
    h();  
    return 0;
}

The output as expected.

f char
f E
f E
f char
f char
f char

Thanks,

Jeffrey

Cacilie answered 3/10, 2011 at 3:29 Comment(1)
Interestingly, Clang 2.9 appears to have the same issue than gcc.Jarodjarosite
C
3

As noted in the first example, this is an instance of two-phase name lookup, which both GCC and Clang implement but MSVC does not. And in this case, both GCC and Clang are correct: it's actually the standard that is wrong, as noted in C++ core defect report #197. The C++11 standard contains a different example.

This is one of the most common problems we see when porting code to Clang from either MSVC (which never implemented two-phase name lookup) or from GCC (which didn't implement two-phase name lookup uniformly until recently).

Crossbench answered 11/10, 2011 at 0:14 Comment(0)
D
5

What you're running into is the fact that Visual Studio does not implement two-phase lookup. They only look up the actual name when you instantiate the template.

And Microsoft has pretty much decided at this point that they're not interested in supporting two-phase lookup.

Discontinuance answered 3/10, 2011 at 4:8 Comment(1)
MSVC is a lost cause, however I am surprised that gcc and Clang would get it wrong.Jarodjarosite
C
3

As noted in the first example, this is an instance of two-phase name lookup, which both GCC and Clang implement but MSVC does not. And in this case, both GCC and Clang are correct: it's actually the standard that is wrong, as noted in C++ core defect report #197. The C++11 standard contains a different example.

This is one of the most common problems we see when porting code to Clang from either MSVC (which never implemented two-phase name lookup) or from GCC (which didn't implement two-phase name lookup uniformly until recently).

Crossbench answered 11/10, 2011 at 0:14 Comment(0)
M
2

I don't know what to tell you except that I would agree with you that this is incorrect behavior.

I think what's probably happening is that in the case of MSVC, the compiler is optimizing away an extra pass at the cost of ending up with the knowledge of a later-defined function that should not be use in the case of non-template calls. I must confess, I don't get how GCC/LLVM would end up with the results that they do, as the results are what you would expect as the exception and not the rule.

I guess I'd file it as a bug on http://bugreport.apple.com/ and http://connect.microsoft.com/ and see what they say?

Molybdenous answered 3/10, 2011 at 3:47 Comment(6)
Thanks, Mahmoud. Please keep me updated if there is any comment.Cacilie
@Jeffrey: I think he was suggesting that you file the bug. :)Greet
g++ 4.6.1 exhibits the same behavior as Xcode (which has the comparatively ancient g++ 4.2), so please file a bug report at gcc.gnu.org/bugzilla as well.Florentinaflorentine
@Zack, I think your bug report is wrong, both expected/actual show the same results.Sella
If it's wrong, I'm confident the GCC C++ people will say so :)Florentinaflorentine
I logged this issue to apple and microsoft respectively. bugreport.apple.com/cgi-bin/WebObjects/RadarWeb.woa/28/wo/… connect.microsoft.com/VisualStudio/feedback/details/692932Cacilie

© 2022 - 2024 — McMap. All rights reserved.