Suppose we have a class template with default template parameter:
template <typename T = int>
class Foo {};
We can omit angle brackets when creating a variable inside a function:
int main()
{
Foo a; // gets properly deduced as Foo<int>
}
But we can't do that for member variables:
struct S
{
Foo a; // Deduce Foo<int>
};
We can't have derivative types such as this:
Foo* ptr; // Foo<int>*
Foo& ref; // Foo<int>&
int Foo::* mem_ptr; // int Foo<int>::*
std::function<Foo(const Foo&)> fn; // std::function<Foo<int>(const Foo<int>&)>
We can't accept parameters and return them:
Foo Bar(const Foo&); // Foo<int> (*)(const Foo<int>&)
Why? Is this considered a bug in the standard? Is there a proposal to fix it? Are there any actual problems with omitting angle brackets?
My use case:
I have a class template which provides default argument. The template parameter is an expert-only feature that I myself never use but it is there for those 1% of experts who want that total flexibility. Now for other 99% I want to hide the fact that Foo is actually a class template but it doesn't work because users have to type Foo<> when declaring it as a member variable, current solution is this:
template <typename T = int>
class BasicFoo {};
using Foo = BasicFoo<>;
But it complicates implementation code and is not elegant at all.
Is this considered a bug in the standard?
No.
Templates are a named construct which generates another construct (classes/functions/variables) based on a set of parameters. The name of a template is not the name of the construct which it generates. The name of a template is just the name of the template; to name the thing the template generates, you must provide the template parameters.
Foo is the name of a template; Foo<> is the name of a class generated by that template and its associated template parameters.
There are a couple of places where C++ allows a template to be used in such a way that its parameters are deduced from a sequence of expressions. But these are very specific places, created for convenience purposes. They do not exist for the purpose of hiding the fact that a name represents a template rather than the generated construct.
Is there a proposal to fix it?
There is nothing broken to fix. And there are at present no proposals adding changes in this way.
Are there any actual problems with omitting angle brackets?
Define "actual problem". Is it theoretically possible to have the language altered so that, if all of a template's parameters are defaulted, the name of the template can be used without template parameters to simultaneously mean the template and the thing the template generates?
It is probably possible. But it would be complicated to specify. You would need a serious spec-doctor, one who understands the C++ grammar at a deep level, to know for sure whether it is possible, and what exactly would need to be changed to do it.
But at the end of the day, it would only ever be useful for a small, select set of templates: templates that have default values for all of its parameters. The real question is whether that is a common enough case to be worth the effort.
I don't consider myself a language expert, but my stance would be that the problem your proposal tries to tackle is solved much simpler in the same way std::(basic_)string does it.
We have
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
> class basic_string;
and then a set of typedefs for "non-expert" users, such as std::string for std::basic_string<char>.
If an expert user wants to make use of the other template parameters, they can themselves define a type alias, which is nice and coherent with the above. Moreover, this cleanly separates the template from the types that are created from it.
Your suggestion of allowing templates with defaults for all parameters to be named by MyTemplate alone, instead of requiring MyTemplate<>, or making use of using MyTemplate = MyBasicTemplate<>;, has the following issues:
It complicates the grammar and specification of the language. You need to touch the allowed syntax of all the contexts mentioned in your question, adding the ability to use a template name where a type name would be expected, but only if the relevant template has default values for all template parameters. And if you don't change all of them, you introduce weirdly inconsistent behavior.
There is some overlap between your suggestion and CTAD, but CTAD is decidedly about reducing type verbosity for initialization. CTAD offers significant comfort within its scope and is extensible through deduction guides, whereas your proposal's syntactic sugar is only relevant in a tiny usage niche, with much smaller benefits.
There is the danger of accidentally using the wrong template parameters (did you mean the default template parameters or did you just forget to specify the ones you wanted?). Even if that is not a concern in your use case, the standard would have to concern itself with that potential issue.
There is also the danger of your suggestion conflicting with deduction guides. Who should win?
Your problem is easily and conveniently solved with existing language tools (see above). I disagree that this "complicates" implementation code (complexity is literally only increased by a single typedef/using, (re)naming your template is absolutely trivial work) or that it is inelegant.
Overall, the problem you intend to solve (saving library implementers a using, or users a <> (or using), exclusively for all-defaulted templates) is fringe at best and will not be a sufficient motivation for significantly altering several core aspects the language. That's my prediction at least.
Related
Suppose we have a class template with default template parameter:
template <typename T = int>
class Foo {};
We can omit angle brackets when creating a variable inside a function:
int main()
{
Foo a; // gets properly deduced as Foo<int>
}
But we can't do that for member variables:
struct S
{
Foo a; // Deduce Foo<int>
};
We can't have derivative types such as this:
Foo* ptr; // Foo<int>*
Foo& ref; // Foo<int>&
int Foo::* mem_ptr; // int Foo<int>::*
std::function<Foo(const Foo&)> fn; // std::function<Foo<int>(const Foo<int>&)>
We can't accept parameters and return them:
Foo Bar(const Foo&); // Foo<int> (*)(const Foo<int>&)
Why? Is this considered a bug in the standard? Is there a proposal to fix it? Are there any actual problems with omitting angle brackets?
My use case:
I have a class template which provides default argument. The template parameter is an expert-only feature that I myself never use but it is there for those 1% of experts who want that total flexibility. Now for other 99% I want to hide the fact that Foo is actually a class template but it doesn't work because users have to type Foo<> when declaring it as a member variable, current solution is this:
template <typename T = int>
class BasicFoo {};
using Foo = BasicFoo<>;
But it complicates implementation code and is not elegant at all.
Is this considered a bug in the standard?
No.
Templates are a named construct which generates another construct (classes/functions/variables) based on a set of parameters. The name of a template is not the name of the construct which it generates. The name of a template is just the name of the template; to name the thing the template generates, you must provide the template parameters.
Foo is the name of a template; Foo<> is the name of a class generated by that template and its associated template parameters.
There are a couple of places where C++ allows a template to be used in such a way that its parameters are deduced from a sequence of expressions. But these are very specific places, created for convenience purposes. They do not exist for the purpose of hiding the fact that a name represents a template rather than the generated construct.
Is there a proposal to fix it?
There is nothing broken to fix. And there are at present no proposals adding changes in this way.
Are there any actual problems with omitting angle brackets?
Define "actual problem". Is it theoretically possible to have the language altered so that, if all of a template's parameters are defaulted, the name of the template can be used without template parameters to simultaneously mean the template and the thing the template generates?
It is probably possible. But it would be complicated to specify. You would need a serious spec-doctor, one who understands the C++ grammar at a deep level, to know for sure whether it is possible, and what exactly would need to be changed to do it.
But at the end of the day, it would only ever be useful for a small, select set of templates: templates that have default values for all of its parameters. The real question is whether that is a common enough case to be worth the effort.
I don't consider myself a language expert, but my stance would be that the problem your proposal tries to tackle is solved much simpler in the same way std::(basic_)string does it.
We have
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
> class basic_string;
and then a set of typedefs for "non-expert" users, such as std::string for std::basic_string<char>.
If an expert user wants to make use of the other template parameters, they can themselves define a type alias, which is nice and coherent with the above. Moreover, this cleanly separates the template from the types that are created from it.
Your suggestion of allowing templates with defaults for all parameters to be named by MyTemplate alone, instead of requiring MyTemplate<>, or making use of using MyTemplate = MyBasicTemplate<>;, has the following issues:
It complicates the grammar and specification of the language. You need to touch the allowed syntax of all the contexts mentioned in your question, adding the ability to use a template name where a type name would be expected, but only if the relevant template has default values for all template parameters. And if you don't change all of them, you introduce weirdly inconsistent behavior.
There is some overlap between your suggestion and CTAD, but CTAD is decidedly about reducing type verbosity for initialization. CTAD offers significant comfort within its scope and is extensible through deduction guides, whereas your proposal's syntactic sugar is only relevant in a tiny usage niche, with much smaller benefits.
There is the danger of accidentally using the wrong template parameters (did you mean the default template parameters or did you just forget to specify the ones you wanted?). Even if that is not a concern in your use case, the standard would have to concern itself with that potential issue.
There is also the danger of your suggestion conflicting with deduction guides. Who should win?
Your problem is easily and conveniently solved with existing language tools (see above). I disagree that this "complicates" implementation code (complexity is literally only increased by a single typedef/using, (re)naming your template is absolutely trivial work) or that it is inelegant.
Overall, the problem you intend to solve (saving library implementers a using, or users a <> (or using), exclusively for all-defaulted templates) is fringe at best and will not be a sufficient motivation for significantly altering several core aspects the language. That's my prediction at least.
If you mistakenly do something like:
#include<limits>
int arr[3];
auto x = std::numeric_limits<decltype(arr[0])>::max();
You will get unhelpful error message from the file in the STL implementation.
Problem is that template argument is a reference, so the fix is to remove it:
auto x = std::numeric_limits<std::remove_reference_t<decltype(arr[0])>>::max();
Now my question is why numeric_limits do not know to do this by themselves?
I would understand that you do not want to remove pointerness(since max of char pointer and max of char are very very different things), but I would assume that whenever you have a reference as an argument to numeric_limits you would be happy with result that is obtained by removing it.
From a technical point of view, there is no reason why std::numeric_limits<T> couldn't work with references. All what would be needed it to add a partial specialisations like this:
namespace std {
template <typename T> struct numeric_limits<T&>: numeric_limits<T> {};
template <typename T> struct numeric_limits<T&&>: numeric_limits<T> {};
template <typename T> struct numeric_limits<T const>: numeric_limits<T> {};
template <typename T> struct numeric_limits<T volatile>: numeric_limits<T> {};
template <typename T> struct numeric_limits<T const volatile>: numeric_limits<T> {};
}
A user can't add these specialisations, of course. However, that's not a huge constraint as a custom variant of numeric_limits can be created in a suitable namespace.
As it is technically doable the question now becomes why the standard doesn't provide these declarations. I don't think there will be a conclusive answer (unless this idea was discussed and discarded with a suitable and still accessible record). Here are some of the potential answers:
The feature wasn't proposed. When std::numeric_limits was introduced it specifically targeted replacing the macros in <limits.h> with a more a C++ approach. Something like decltype(expr) and forwarding references didn't exist, i.e., template arguments wouldn't be "accidentally" deduced as reference types. Thus, removing qualifiers wasn't a concern at the time.
I'm not sure if at the point in history when numeric_limits were added partial template specialisation already existed. Even if it existed, anything resembling template meta programming didn't exist. As a result, it may not have been possible or assumed to be possible to meddle with the template argument type in the necessary way.
Even if it were considered, I doubt the committee would have gone with adding the partial specialisations: numeric_limits<T> inspects the traits of type T but reference types don't have a max() or digits. Also, if reference types are supported because "clearly" the desired property must be the one of the underlying type where to stop: should std::numeric_limits<int*>::max() provide the same value as std::numeric_limits<int>::max(), too? After all, it also doesn't make any sense on pointers.
Considering that the original proposal almost certainly didn't cover the case of qualified types (see above), another reason why the feature isn't available is that it simply wasn't proposed: without a proposal the standard won't get changed. Whether the standard would get changed if the feature were proposed is a separate question. There is a proposal in this general space (P0437r0) but browsing over it I don't think this proposal covers qualified types, either.
If I expect a template template parameter to have one arguement then I could declare it as follows:
template<template<typename> class T>
struct S {
T<int> t_;
//other code here
}
however if I later want to provide a template template parameter which takes two arguements where the second has a default value (like std::vector) T<int> t_; would still work but the template would not match template<typename> class T. I could fix this by making template<typename> class T into a variadic template template template<typename...> class T. Now my code is more flexible.
Should I make all my template template parameters variadic in future? Is there any reason I should not (assuming C++11 support is already required for my code for other reasons)?
First, documentation. If the parameter is variadic, the user now needs to check some other source to find out that this really wants something that will takes one template parameter.
Second, early checking. If you accidentally pass two arguments to T in S, the compiler won't tell you if it's variadic until a user actually tries to use it.
Third, error messages. If the user passes a template that actually needs two parameters, in the variadic version the compiler will give him an error message on the line where S instantiates T, with all the backtrace stuff in-between. In the fixed version, he gets the error where he instantiates S.
Fourth, it's not necessary, because template aliases can work around the issue too.
S<vector> s; // doesn't work
// but this does:
template <typename T> using vector1 = vector<T>;
S<vector1> s;
So my conclusion is, don't make things variadic. You don't actually gain flexibility, you just reduce the amount of code the user has to write a bit, at the cost of poorer readability.
If you know already that you will need it with high probability, you should add it. Otherwise, You Ain't Gonna Need It (YAGNI) so it would add more stuff to maintain. It's similar to the decision to have a template parameter in the first place. Especially in a TDD type of environment, you would refactor only when the need arises, rather than do premature abstraction.
Nor is it a good idea to apply rampant abstraction to every part of
the application. Rather, it requires a dedication on the part of the
developers to apply abstraction only to those parts of the program
that exhibit frequent change. Resisting premature abstraction is as
important as abstraction itself
Robert C. Martin
Page 132, Agile Principles, Patterns and Practices in C#
The benefits of variadic templates can be real, as you point out yourself. But as long as the need for them is still speculative, I wouldn't add them.
The following code yields an error error: ‘struct Foo’ is not a valid type for a template constant parameter:
template <struct Foo>
struct Bar {
};
Why is that so?
template <class Foo>
struct Bar {
};
works perfectly fine and even accepts an struct as argument.
This is just an artifact of the syntax rules - the syntax just lets you use the class or typename keywords to indicate a type template parameter. Otherwise the parameter has to be a 'non-type' template parameter (basically an integral, pointer or reference type).
I suppose Stroustrup (and whoever else he might have taken input from) decided that there was no need to include struct as a a keyword to indicate a type template parameter since there was no need for backwards compatibility with C.
In fact, my recollection (I'll have to do some book readin' when I get back home) is that when typename was added to indicate a template type parameter, Stroustrup would have liked to take away using the class keyword for that purpose (since it was confusing), but there was too much code that relied on it.
Edit:
Turns out the story is more like (from a blog entry by Stan Lippman):
The reason for the two keywords is
historical. In the original template
specification, Stroustrup reused the
existing class keyword to specify a
type parameter rather than introduce a
new keyword that might of course break
existing programs. It wasn't that a
new keyword wasn't considered -- just
that it wasn't considered necessary
given its potential disruption. And up
until the ISO-C++ standard, this was
the only way to declare a type
parameter.
Reuses of existing keywords seems to
always sow confusion. What we found is
that beginners were [wondering]
whether the use of the class
constrained or limited the type
arguments a user could specify to be
class types rather than, say, a
built-in or pointer type. So, there
was some feeling that not having
introduced a new keyword was a
mistake.
During standardization, certain
constructs were discovered within a
template definition that resolved to
expressions although they were meant
to indicate declarations
...
The committee decided that a new
keyword was just the ticket to get the
compiler off its unfortunate obsession
with expressions. The new keyword was
the self-describing typename.
...
Since the keyword was on the payroll,
heck, why not fix the confusion caused
by the original decision to reuse the
class keyword. Of course, given the
extensive body of existing code and
books and articles and talks and
postings using the class keyword, they
chose to also retain support for that
use of the keyword as well. So that's
why you have both.
You can instantiate a template using a struct; however, the syntax for declaring a template type only allows the keywords "class" or "typename" to appear where you are attempting to use the keyword "struct".
I should add that you can also use a specific type (e.g. int), if you want to instantiate your template based on a compile-time constant or based on an object with external linkage... but that's somewhat of an aside.
The short answer is: template <class Foo> even accepts a union or a double - still, neither is allowed instead of class. However, typename is. That's just the way the syntax was defined.
A somewhat longer answer: When templates for C++ where "invented", there was a keyword needed at that place saying that the next identifier would be a type name. It was decided to re-use the existing class keyword. That was a bit confusing, but there's a general reluctance to introducing more keywords, because they always break some existing code which used this as an identifier when it wasn't a keyword.
Later, typename became a keyword for other reasons, and since it is a much better fit, it can now be used in that place: template <typename Foo>. However, with billions of lines of code out there using class in that place, it must remain valid for that purpose. So now both are allowed.
As is common in C++, this created several camps as to what keyword to use in that place. Some stick with class, because they've been using it for more than a decade. Others prefer typename, because it's a much better fit. Some use class when Foo is expected to be of a class type (members are accessed) and typename when built-ins can be used, too.
Because the keyword for template parameters is class or typename. This doesn't restrict the Foo parameter to be a class - it can be of any type.
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.
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.
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;
}
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.
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.