tags:

views:

152

answers:

4
+3  Q: 

template errors

Hi,

I heard C++ templates wont generate errors until they are used. Is it true ? Can someone explain me how they work ?

A: 

From here,

From the point of view of the compiler, templates are not normal functions or classes. They are compiled on demand, meaning that the code of a template function is not compiled until an instantiation with specific template arguments is required. At that moment, when an instantiation is required, the compiler generates a function specifically for those arguments from the template.

Hope it helps..

liaK
In general terms of a template class, this means that the compiler will generate a different type for each type of template class declared. Hence `vector<int>` is **not the same** as `vector<double>`. If you don't declare any `vector` types, then there are no types generated.
Duracell
@ Duracell, Yeah I agree and that's what meant by, *compiler generates a function **specifically** for those arguments...*
liaK
+1  A: 

They generate compiler errors when they are compiled. They are compiled separately for each actual parameter passed as the template argument(s) (this is unlike Java Generics), e.g., if I have:

template <typename T> class foo { ... }

and

int main() {
  foo<char> c;
  foo<int> i ;
}

the template foo gets compiled twice, once for chars, once for ints.

If you never (directly or indirectly) instantiated or used template foo, it wouldn't be compiled and you'd not see any compiler errors.

Once compiled, they're just "normal" C++ code, and like any code, can generate runtime errors.

tpdi
This still doesn't allow for syntactically invalid code which will raise errors in the first pass.
Georg Fritzsche
Part of the confusion probably comes from the fact, that VC++ skips the first pass, so you can put any gibberish in the templates as long as they are not instantiated.
UncleBens
Georg, does the Standard require a first pass?
tpdi
@tpdi: yes it does, VC++ is not compliant.
Matthieu M.
@tpdi: see comments on above answer; standard describes such checks but clearly states they do not affect compliance.
Potatoswatter
@Potatoswatter: I wouldn't call that "clearly". For example, 14.6/8 says "If a name does not depend on a template-parameter (as defined in 14.6.2), a declaration (or set of declarations)for __that name shall be in scope___ at the point where the name appears in the template definition". Now, my understanding of standardeze is just about as bad as it gets, so I have to ask: What is a compiler supposed to do when the standard says "X shall be declared" and said X isn't declared for a program?
sbi
@sbi: It issues a diagnostic. But as 14.6/7 says (which is what we're discussing), diagnostics will certainly be issued when the template is instantiated. The standard goes to some length to prohibit illegal premature diagnostics that reject valid templates, in language such as 14.6/8. But 14.6/8 doesn't require any diagnostics.
Potatoswatter
@Potatoswatter: But then _not instantiating_ the template would prevent the compiler from complaining - which is what I think this piece of text rules out.
sbi
@sbi: 14.6/9 (which is what you quoted) doesn't require a diagnostic. How does it rule out anything? Did you look at the examples at 14.6/7? It says `+; // may be diagnosed even if X::g is not instantiated` - note the *may be*
Potatoswatter
@Potatoswatter: I think by now we all got your oint about 14.6/7, but it seems you keep missing my point about 14.6/9, where, according to your comment above, _the standard requires a diagnostic_ for using something that isn't declared - even if a template isn't instantiated. If this isn't requiring two-phase lookup of identifiers used in templates, I'd like to know why.
sbi
@sbi: 14.6/7 is very generally worded, and its example reinforces its generality. Do you expect notes in [expr] saying that expressions within templates don't need to be checked? To just what kind of errors do you think the second and third sentences apply?
Potatoswatter
@Potatoswatter: One _last_ time: __I understood what you said about 14.6/7.__ You do have a point there. Nevertheless, 14.6/9 demands non-dependent identifiers to be declared before used in a non-instantiated template. For that to detect, you need two-phase parsing. And now you have the opportunity to repeat 16.6/7 one more time without me interfering. I'm outta here.
sbi
@sbi you don't need two phase parsing to detect that. After the first parse, you know that `X` is/is not declared, and *may* emit the diagnostic (non-dependent names are bound when template is parsed!). If one paragraph says "this is il-formed", and another paragraph takes this and refines it, the refinement is in effect. As is with SFINAE, for example. Still, @Potatoswatter's example of `+;` is ill - see my comment to @chubsdad answer. "And now you have the opportunity to repeat 16.6/7 one more time without me interfering. I'm outta here" -> i so understand this action of yours :)
Johannes Schaub - litb
@sbi: See the discussion Johannes linked in Chubsdad's thread… two-phase parsing makes it easier to enforce 14.6/9, but it's not impossible otherwise, just harder. You have to carefully save the template's enclosing scope. If Microsoft does this improperly, then they are noncompliant, but it's not because of 1-phase vs 2-phase per se.
Potatoswatter
@Johannes: So after FF recovering from a sudden reboot, this is one of the old URLs which it opened. So I read it. `<sigh>` Did you actually really write "[...] you don't need __two-phase parsing__ [...]. After __the first parse__ [...]"? [The emphasize is mine.] That's plain silly.
sbi
@sbi nice browser you have. :) Though, why is it silly? For `/* a not declared here */ template<typename T> void f() { a++; }` you can diagnose that template at template definition time. You don't need to wait until it is instantiated. You claimed earlier "For that to detect, you need two-phase parsing.". Actually, I just kept your terminology, if that's what you are on - I agree it should read "two-phase name lookup".
Johannes Schaub - litb
@Johannes: Let me repeat, using my own words: "You don't need __two__ -phase parsing. ... When doing the __first__ parse...." I guess, technically, that first sentence doesn't rule out three-phase parsing (or n-phase, FTM). But other than that, if you have a _first_ parse, there surely will be a second, won't it?
sbi
@sbi now you are just kidding LOL. I suspected you were kidding before, but now i *know*. Ahaha
Johannes Schaub - litb
@Johannes: I'm deep into my second glass of a pretty decent supermarket Tempranillo, but other than that I cannot imagine why you're laughing at me.
sbi
+6  A: 

Templates follow two phase compilation model.

struct X{
private:
   void f(){}
};

template<class T> void f(T t){
   int;   // gives error in phase 1 (even if f(x) call is commented in main)
   t.f(); // gives error only when instantiated with T = X, as x.f() is private, in phase 2
}

int main(){
   X x;
   f(x);
}
Chubsdad
Actually, the standard mandates that template follow the two phases compilation model, but some compilers (most notably VS) do not and thus only give warning / errors during the second phase (unless it's just so bad that the parser chokes).
Matthieu M.
@Mattieu and chubsdad: Is there a particular place in the standard where this is described?
Potatoswatter
Refer Section 10.3 of C++ Templates by David Vandervoorde. Section 14.6 actually talks indirectly about this concept. e.g. "[Note: if a template is instantiated, errors will be diagnosed according to the other rules in this Standard. Exactly when these errors are iagnosed is a quality of implementation issue. ]"
Chubsdad
@Matthieu and chubsdad: Found it. 14.6/7: "If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required." So it is not a compliance issue at all. The standard strongly suggests such checks and declares that such support improves "quality of implementation," but it is also clear about diagnostics on non-instantiated templates being not required. The paragraph is not changing for C++0x. I found some articles with a contrary view, but poor referencing; I would call those FUD.
Potatoswatter
@chubsdad: if the standard doesn't talk about something **directly**, it's not in there.
Potatoswatter
@Potatoswatter: TTBOMK, the standard nowhere says _directly_ that a `char` has to have at least 8bits, still this can be inferred from indirect mentioning. So, while you might or might not be right _particularly_ about two-phase lookup not existing because it isn't mentioned, you seem to be wrong wrong _in general_.
sbi
@sbi: "Objects declared as characters (char) shall be large enough to store any member of the implementation’s basic character set." and "The basic source character set consists of 96 characters" with simple mathematical inference lead to that conclusion. No logical inference applies to two-phase instantiation being a requirement; the concept is outlined and clearly marked optional. The reason for emphasizing directness is this hand-wavy article "Standard Features Missing From VC++ 7.1. Part III: Two-Phase Name Lookup." I'm not one to come to the defense of Microsoft, but it's BS.
Potatoswatter
@Potatoswatter: Well, for one, in my universe 7bit is enough to represent 97 different values. Anyway, if inference is allowed, then __direct mentioning__ isn't required. Which exactly is what I was struggling to say.
sbi
@sbi: fine, I took a little shortcut because I didn't feel like referring to C99 as well. What I am struggling to say is that inference is a different thing from insinuation. That article doesn't seem to quote anything from the standard (I didn't read it all closely, though), it merely cites the entire standard at as a source. Chubsdad, somewhat likewise, used hand-waviness in saying "Section 14.6 actually talks indirectly about this concept". Inference still requires "talking" directly about the concept. Which the standard does in the case of 2-pass templating, and it contradicts the article.
Potatoswatter
@Potatoswatter, compilers such as VC++ however even accept *syntactically* nonsense as valid templates. That paragraph does not allow such acceptance (and the example to that paragraph, which shows accepting `+;` is wrong - it is nonnormative anyway). Here, the intention of the Standard is not reflected by normative wording. See comp.lang.c++.moderated extensive discussion with Walter Bright here: http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/78078fcf846f8859?pli=1
Johannes Schaub - litb
@Johannes: Then as I asked sbi in the other thread on this page, what *does* it mean? And Mr Bright in your linked discussion is clearly on my side: "The Standard allows precompiling them into some syntactical intermediate form, and there are some crutches in the syntax to allow this to work. … The Standard allows for either method." Late in the discussion he concludes "Right, so it's not required." Do you anything more specific or any evidence the example is in error?
Potatoswatter
@Potatoswatter it *intends* to allow `+;`, but it does not explain how it can be allowed nor it says that it is allowed in the first place (it only does so in the example). It *does* say compilers are allowed to not diagnose `template<typename T> void f() { notdefined(); }` upfront. I think that this is an easy matter, but people tend to complicate this paragraph (see that discussion linked, it details everything out entirely). Mr Bright has a point. "The Standard allows" should mean "The Standard intends to allow", but not "The Standard allows by its letters".
Johannes Schaub - litb
@Potatoswatter in the end you see he agrees with me that his compiler is wrong when interpreting the Standard to the letter, rather than interpreting it according to its intent. There is no clearer wording than there is. The matter is extremely simple and clear: The example is clearly not following the normative text.
Johannes Schaub - litb
Ah, now I see there is another page to it. Mr Bright doesn't entirely change is mind, but says "perhaps," and some official-looking corrections including removal of `+;` as it precludes the example from being well-formed *anything*. That is a good point, and it gets down to how a 1-phase engine should decide a template's span. However, braces do practically work just fine for such purpose, even if Mr Ivchenkov's point holds about the lack of a simple underlying grammar to support it.
Potatoswatter
@Johannes: wow, you responded already, I spent more time on this than I realized. Well, I guess we're in agreement about `+;`, as for the rest, whatever.
Potatoswatter
A: 

Conceptually, at the highest level

template <Type value, class Y, ...>
...fn-or-class...

may be usefully compared to

#define FN_OR_CLASS(VALUE, TYPE_Y, ...) \
...fn-or-class...

Both basically wait until called/instantiated then substitute the specified types and values to produce tailored code with full compile-time optimisation for those values. But, templates differ from #defines in that they're proper compile-stage constructs that can be enclosed in namespaces, must satisfy the lexer, and not all of a class template is generated when the first instantiation is seen - rather, functions are generated on an as-needed basis.

When the compiler first encounters a template, it does a rough check that the template's content could make sense for some hypothetical instantiation. Later, when it encounters a specific instantiation, then for class templates only the functions that are used are further checked to make sure they can be compiled with the specific parameters in use. This does mean that a class template can appear - for some limited usage - to support instantiation with specific parameters, but if you start using some other functions in the template API then suddenly you can find that it can't be compiled with that presumed-suitable parameter... can force you to redesign your usage rather late in the day. That is one of the reasons that C++0x had planned to introduce Concepts: they elegantly allow templates to check that parameters meet all the template's expectations - if they allow any instantiation, then the user can assume that the full API of the template can be used.

template <class T>
struct X
{
    void f() { }
    void g() { T::whatever(); } // only error if g() called w/o T::whatever
};

int main()
{
    X<int> x;
    x.f();
    // x.g(); // would cause an error as int::whatever() doesn't exist...
}

The SFINAE (substitution failure is not an error) technique can then allow the compiler to select between multiple nearly-matching functions based on the actual instantiating template parameters. This can be used to implement basic compile-time introspection, such as "does this class have a member function fn(int)?".

Tony
"… it does a rough check that the template's content…" - which may result in errors.
Potatoswatter