views:

436

answers:

4

Having read the claim multiple times in articles - I want to add this question to Stackoverflow, and ask the community - is the following code portable?

template<template<typename T, typename Alloc> class C>
void f() {
  /* some code goes here ... */
}

int main() {
  f<std::vector>();
}

Is the implementation that supplies std::vector really allowed to have additional, defaulted template parameters beyond the two well known ones? This would render the above code ill-formed, as it assumes two template parameters. See the last paragraph in this article for an example of such a claim.

+11  A: 

I found the following issue report, which says

There is no ambiguity; the standard is clear as written. Library implementors are not permitted to add template parameters to standard library classes. This does not fall under the "as if" rule, so it would be permitted only if the standard gave explicit license for implementors to do this. This would require a change in the standard.

The LWG decided against making this change, because it would break user code involving template template parameters or specializations of standard library class templates.

The books and people that say an implementation may add other optional parameters seem to be wrong.

Johannes Schaub - litb
A: 

Check subsubsections of 17.4.4 [lib.conforming].

17.4.4.3/3 says that "a global or non-member function cannot be declared by the implementation as taking additional default arguments", but 17.4.4.4/2 explicitly allows replacing described member function signatures with longer ones so long as the additional parameters have defaults.

There's no section for templates, though, so if they felt the need to provide 17.4.4.3/3, it seems to me like extra template parameters are allowable barring wording to the contrary.

me22
Ok, never mind -- an issue report is obviously better than my guess :P
me22
A: 

I have seen this claim, too. But.

For one, I have never seen an implementation doing this. I seem to remember that Andrei Alexandrescu once contemplated using things like allocator types on steroids (something like my_fancy_thing<std::allocator,more_info_to_pass_to_the_container>, while just std::allocator would still work, too). But even this would still keep your f() working, and that's the closest thing to an implementation breaking your example I have ever heard being discussed.

I think this falls pretty much into the same category as the claim that a 0 pointer does not necessarily have to be represented by a value with all bits set to zero - even if vendors really have that freedom (which I don't know, since there are claims from both sides, too), they won't ever use it, because that would break basically all existing code.

So I have long since decided to not to worry about it.

sbi
+1  A: 

Incredibly, I was recently reading "C++ Templates: The Complete Guide," and last book marked the following on page 111:

A template template argument must be a class template with parameters that exactly match the parameters of the template template parameter it substitutes. Default template arguments of a template template argument are ignored (but if the template template parameter has default arguments, they are considered during the instantiation of the template).

So, if the book is to be believe, your example where non-standard default parameters are added to std::vector would be legal - since default template arguments of a template template argument are ignored.

As a real world test, I compiled the following in g++ (successfully) and Visual Studio 2008 (failed on the mismatched parameters):

template<typename T1, typename T2, typename T3 = float>
class MyClass
{
public:
    T1 v1;
    T2 v2;
    T3 v3;
};

template<template<typename T1, typename T2> class C>
void f()
{
    C<int,double> *c = new C<int,double>();
}

int main ()
{
    f<MyClass>();
    return 0;
}
csj
What they mean with "Default template arguments of a template template argument are ignored" is that even though a parameter may have a default argument, that default argument is not used and the corresponding parameter counts as if it didn't have a default argument. So if the impl would have a vector with 3 parameters, with the 3rd having a default argument, then passing vector as a template template argument is as if that default argument weren't there.
Johannes Schaub - litb
I tried to compile the example with g++4.4.1 - it rejects, while g++4.1 accepts the code. This looks like it was a bug in GCC.
Johannes Schaub - litb
Thanks for the clarification on that passage - I was interpreting it a bit differently. I had compiled on 4.1 (an older Fedora VM I had kicking around).
csj
Example: `template<typename A, typename B = int> struct E { }; template<template<typename AA, typename BB = float> class C> void f() { C<bool> c; } int main() { f<E>(); }` <- when passing `E`, it's like `B` had no default argument (`E` is the "template template argument", and its default argument "int" is ignored - but not the corresponding parameter). But when instantiating in `f`, then the default argument "float" of the "template template parameter" is used. Difference between parameter and argument is crucial in that quote.
Johannes Schaub - litb