compiler discrepancy: use of undefined type

Started by
8 comments, last by the_edd 11 years, 8 months ago

#include <iosfwd>

struct foo { };

std::ostream &operator<< (std::ostream &out, const foo &) { return out; }

void format(std::ostream &out, const foo &x) { out << x; }

int main()
{
return 0;
}


Comeau online and g++ 4.6 both accept this code without issue. Visual C++ 2010 complains:


P:\guff>cl /nologo /EHsc /W3 ostream_fwd.cpp /Feostream_fwd.exe
ostream_fwd.cpp
ostream_fwd.cpp(7) : error C2027: use of undefined type 'std::basic_ostream<_Elem,_Traits>'
with
[
_Elem=char,
_Traits=std::char_traits<char>
]


Who's correct? If someone could try this on the latest Visual C++ compiler, I'd be interested to see what it says/does.
Advertisement
VS2012 RC gives the same error. Clang also compiles without error. Based on that I would say that VS is probably wrong, but I think it's a bit of a grey area whether the compiler should want the definition of a type when evaluating operators for which there is an explicit external operator defined, so I would personally err on the side of them being right.
It wouldn't surprise me if this is one of many aspects of C++ that is left up to the implementers to clarify. Unfortunately I don't have the brainpower or patience to go standards-diving for the answer, so I'll just speculate uselessly :-P

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

edd, are you sure you're not including ostream or other library by mistake? That library would fix this error.

According to MSDN and cpp.reference.com and even gcc, iosfwd contains only forward declaration, making me believe you're the one who made mistake.

Sources:
http://msdn.microsoft.com/en-us/library/1af12yty%28v=vs.110%29.aspx
http://en.cppreference.com/w/cpp/header
http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.1/group__s27__2__iosfwd.html

According to MSDN and cpp.reference.com and even gcc, iosfwd contains only forward declaration, making me believe you're the one who made mistake.


That's true, but his code doesn't really use anything that requires the definition of the class, so a forward declaration is all that would be necessary for the compiler to evaluate the expressions.

You can replace that code with this:

struct foo { };

class mynonexistentostream;

mynonexistentostream &operator<< (mynonexistentostream& out, const foo &) { return out; }

void format(mynonexistentostream& out, const foo &x) { out << x; }

int main()
{
return 0;
}


and the result is the same; an error on VS and no error on the other compilers, with no headers being included at all.

A compiler needs to evaluate any class member operators and namespace-scoped operators to determine which one to use, but in this case the namespace-scoped operator is clearly the best possible option, so a compiler would be able to evaluate things without having the class definition. Whether a compiler is actually required to do so per the standard I'm not sure about. There's a very good chance that it's not, and both may be technically compliant.

According to MSDN and cpp.reference.com and even gcc, iosfwd contains only forward declaration,

That's entirely the gist of my inquiry.

Intuitively, I don't think that there's anything in the code that should require the definition, but the standard might require it.


making me believe you're the one who made mistake.
[/quote]
Can you phrase that in terms of standardese? I'll have a go myself on monday when I have my copy to hand.

If anybody can think of a way of tricking Visual C++ in to compiling this code, that would be handy (without #include <ostream>!) smile.png
From MSDN's page on C2027:

It is possible to declare a pointer to a declared but undefined type. But Visual C++ does not allow a reference to an undefined type.
[/quote]
I guess this supports the grey-area hypothesis.
Edit: nevermind, followup coming.
Actually after further investigation, it appears that Visual Studio is patently wrong. The reason for the error is not because of operator resolution issues, it's because of an apparent compiler limitation that does not allow references to undefined types to be returned from a function if they are not assigned. C2027 says that references to undefined types are not allowed, but that is not the case; if their example code is changed to the following it compiles fine:

A& a = CreateA();

It just doesn't work if the return value is unassigned as in their example.

Per section 5.2.2 of the C++ standard, the result type of a function call that returns an lvalue reference is an lvalue, so an incomplete type should be fine.

Since the operator << returns a reference, and it is not being assigned, the same error is being invoked.

A workaround for your code could then be this:

void format(std::ostream &out, const foo &x) { std::ostream& t = out << x; }

which should compile fine.

Update: so, apparently the reason that it doesn't work in VS is because VS is unable to convert an undefined type to type void, and a function with the return value unassigned is equivalent to casting to type void, i.e. (void)CreateA();

The C++ standard states that "Any expression can be explicitly converted to type cv void, in which case it becomes a discarded-value expression."

Therefore VS is wrong and the others are right.

A workaround for your code could then be this:

void format(std::ostream &out, const foo &x) { std::ostream& t = out << x; }

which should compile fine.

Nice find! Thanks!

This topic is closed to new replies.

Advertisement