views:

114

answers:

2

In C++ it's OK to have a funcction that takes a function local type:

int main() {
  struct S { static void M(const S& s) { } };
  S s;
  S::M(s);
}

but not OK to have a template that does:

template<typename T> void Foo(const T& t) { }

int main() {
  struct S { } s;
  Foo(s);   // Line 5: error: no matching function for call to 'Foo(main()::S&)'
}

14.3.1 paragraph 2 in the c++ standard.

A type with no linkage [...] shall not be used as a template-argument for a template type-parameter

Why does C++ disallow that?


The best explanation I've heard so far it that inner types have no linkage and that this could imply that a function that takes them as an arg must have no linkage. But there is no reason I can see that a template instantiation must have linkage.


p.s. Please don't just say "thats not allowed because the standard says it's not"

+1  A: 

I'm guessing it is because it would require the template to be effectively instantiated within the scope of the function, since that is where such types are visible. However, at the same time, template instantiations are supposed to act as if they are in the scope in which the template is defined. I'm sure this it's possible to deal with that somehow, but if I'm right the standards body decided not to put that burden on compiler writers.

A similar decision was the reason vector<vector<int>> is invalid syntax per the standard; detecting that construction requires some interaction between compiler lexer and parser phases. However, that's changing, because the C++0x standards folk found that all the compilers are detecting it anyway to emit sane error messages.

I suspect that if it were to be demonstrated that allowing this construction was trivial to implement, and that it didn't introduce any ambiguities in the language scoping rules, you might someday see the standard changed here too.

Walter Mundt
"Someday" will be as soon as C++0x is ratified, unless they change their mind. The final draft removes this restriction.
Mike Seymour
I could imagine a compiler architecture where it would be hard to implement, but I can also think of ones where it would be had for free.
BCS
+4  A: 

I believe the difficulty that was foreseen was with two instantiations of Foo<T> actually meaning entirely different things, because T wasn't the same for both. Quite a few early implementations of templates (including cfront's) used a repository of template instantiations, so the compiler could automatically instantiate a template over a required type when/if it was found that an instantiation over that type wasn't already in the repository.

To make that work with local types, the repository wouldn't just be able to store the type over which the template was instantiated, but instead it would have to do something like creating a complete "path" to the type for the instantiation. While that's probably possible, I think it was seen as a lot of extra work for little (if any) real benefit.

Since then, the rules have changed enough that the compiler is already required to do something that's just about equivalent, finding (and coalescing) instantiations over the same type at different places (including across TUs) so that two instantiations of foo<int> (for example) don't violate the ODR. Based on that realization, the restriction has been loosened in (the current draft of) C++0x (you still can't instantiate a template class over a local type, but you can use a local type as parameter to a template function).

Jerry Coffin
In short, it used to be more work for the compiler?
BCS
@BCS: While that's probably true also, it's not *so much* that it used to be more work for the compiler, as it is that 1) most of the extra work is now being done for other reasons anyway, and 2) compiling C++ is now so complex anyway that people are more willing to accept adding still more complexity, as long as its not too drastic.
Jerry Coffin
§14.3.1 [temp.arg.type] has several examples of passing local types to a class template.
Potatoswatter