4

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.

  • 2
    Didn't you *already* ask this: https://stackoverflow.com/questions/55026916/ctad-in-member-variables ? – Jesper Juhl Mar 06 '19 at 16:58
  • 2
    @JesperJuhl: No, that's a question about CTAD in member variables. This is something else. – Nicol Bolas Mar 06 '19 at 16:58
  • "But it complicates implementation code and is not elegant at all." You think adding an alias in one spot in code is complex, but adding language rules all over the place is not? – Barry Mar 06 '19 at 17:00
  • @Barry Yes, I think language should help the programmer, not programmer suffering the language. –  Mar 06 '19 at 17:01
  • @Lyberta: "Help the programmer" depends on perspective. A template is not the same thing as what it generates, so is it "helping the programmer" to lie to them? – Nicol Bolas Mar 06 '19 at 17:02
  • Answering the question on it's face value: "Is this considered a bug in the standard? Is there a proposal to fix it?" No, it is not considered a bug, and there is currently no proposal I know of which would change those rules. – SergeyA Mar 06 '19 at 17:02
  • As for helping the programmer, sometime C++ has to deviate from this idea quite significantly. The whole `typename` (when required to indicate an identifier as a type in template contexts) and `foo->template func()` doesn't seem very helpful, but unfortunately, are required. – SergeyA Mar 06 '19 at 17:04
  • 1
    @SergeyA And people like me recognized it as a bug and fixed some cases in C++20: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0634r3.html –  Mar 06 '19 at 17:06
  • That relates to what I have said in my comments in other question of yours - if you feel that you have a solution, by all means, submit a proposal. – SergeyA Mar 06 '19 at 17:08
  • @SergeyA I'm asking the opinion of other language experts before I go forward. –  Mar 06 '19 at 17:10
  • 3
    @Lyberta: "*And people like me recognized it as a bug*" No, they didn't. They recognized a part of the language that could be improved. A "bug" is a part of the language that is *broken*. There's a difference. – Nicol Bolas Mar 06 '19 at 17:10

2 Answers2

3

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
2

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.

Max Langhof
  • 23,383
  • 5
  • 39
  • 72