Tuesday, July 03, 2007

Conforming/Non-conforming non-standard extensions

Recently, I came across this piece of code on the forums where the original poster was claiming to be compiling fine with VS2005 C++ compiler.

[CODE]
class X{};
X f(){
       return X();
}
void g(X&){}
int main(){
       g(f());
       return 0;
}

Quite puzzling, since this is strictly not allowed as per the standards - a temporary cannot be bound to a non-const reference and that is what is actually happening here - the temporary X object returned from f() is being passed to g() that accepts a reference to non-const X.

Could it be because there is no assembly instruction generated for the call in main() since g() is empty? I think not. Any C++ code has to be legal first to get successfully compiled. Optimizations and code generation happens later. One cannot expect a non-compilable copy constructor to be optimized out owing to RVO/NRVO and hence compile fine. That is just wrong. The compiler has to pass the code for semantic validity. The C++ standard lays down the rules around the syntax of a valid C++ program and it must be respected by a conforming compiler. If a particular construct is ill-formed, the compiler may or may not proceed ahead. It would not generate the compiled code though, as the application has ill-formed C++, but it may proceed with the compilation in order to accumulate other errors that might be caught. Imagine a compiler that stopped compiling at the occurence of the very first error. It would make the compilation task so much more repeatative. Not that the following errors are always relevant to different errors but it helps.

Anyways, back to the topic - it came out to be a non-standard extension. Vendors can provide non-standard extensions as the standard allows it. Here is what the standard has to say around non-standard extensions [section : 1.4 (8)]:

"A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs."

And this extension (that violates the standards rule directly) actually fulfils the above except that the diagnostic (error or warning) is not there until a warning level 4.

Here are some additional relevant quotes from the standards [section : 1.4 (2)]:

Although this International Standard states only requirements on C++ implementations, those requirements are often easier to understand if they are phrased as requirements on programs, parts of programs, or execution of programs. Such requirements have the following meaning:

    — If a program contains no violations of the rules in this International Standard, a conforming implementation shall, within its resource limits, accept and correctly execute that program.

    — If a program contains a violation of any diagnosable rule or an occurrence of a construct described in this Standard as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message, except that

    — If a program contains a violation of a rule for which no diagnostic is required, this International Standard places no requirement on implementations with respect to that program.


This extension falls in the second category mentioned above in that it violates the rules of the IS and the IS does not suggest "no diagnosis required". So, the compiler becomes conforming around this extension at warning level 4 but non-conforming at less stricted levels. My recommendation is to never use this extension. If you are going to modify an object, making a named copy of it, would suffice.

References: Codeguru thread - Binding a temporary to a reference to non-const

No comments: