How to avoid specifying arguments for a class template with default template arguments
Asked Answered
T

6

223

If I am allowed to do the following:

template <typename T = int>
class Foo{
};

Why am I not allowed to do the following in main?

Foo me;

But I must specify the following:

Foo<int> me;

C++11 introduced default template arguments and right now they are being elusive to my complete understanding.

Trisyllable answered 12/3, 2013 at 22:52 Comment(0)
S
270

Note:

Foo me; without template arguments is legal as of C++17. See this answer: https://mcmap.net/q/118188/-how-to-avoid-specifying-arguments-for-a-class-template-with-default-template-arguments.

Original answer applicable before C++17:

You have to do:

Foo<> me;

The template arguments must be present but you can leave them empty.

Think of it like a function foo with a single default argument. The expression foo won't call it, but foo() will. The argument syntax must still be there. This is consistent with that.

Somerset answered 12/3, 2013 at 22:53 Comment(8)
@Pubby I suppose it would create some unnecessary complications if Foo might be a template identifier or might be an explicit instantiation depending on whether there's a default argument. Better keep the explicit instantiation syntax. Think of it like a function foo with a single default parameter. You can't call it like foo, you call it with foo(). It makes sense to keep this consistent.Somerset
@sftrabbit but you can't call a function with no arguments like foo either; you can name a class with no arguments as Foo however.Exodus
@aschepler I'm only guessing that they were trying to be consistent, not that they actually are. :DSomerset
@SethCarnegie But that's a class, this is a template. If you instantiate a template, you must give the template arguments (except the situation noted by @aschepler). I know, not the best of arguments.Somerset
@aschepler With a function, the template arguments can be deduced from the function arguments. With a class, it's not possible to decide, whether you meant a template class with default arguments or a non template class.Operable
@OlafDietsche but you can't have a template class and a non-template class with the same name, so the compiler should be able to decide by just looking at what the name is.Exodus
@Pubby The standard committee asked themselves the same, I guess. Now, with C++17, the <> is no more necessary in this case. Check out my answer for more details.Jonquil
It stinks that I can't convert code that uses a Matrix class with floats, and decide later to change it to Matrix<typename T=float> so double's can also be used w/o breaking old code. Std committee got this one wrong.Shinleaf
J
116

With C++17, you can indeed.

This feature is called class template argument deduction and add more flexibility to the way you can declare variables of templated types.

So,

template <typename T = int>
class Foo{};

int main() {
    Foo f;
}

is now legal C++ code.

Jonquil answered 21/6, 2018 at 14:26 Comment(8)
Strange. Just tried it in my C++17 project and it didn't work: "template placeholder type ‘const MyType’ must be followed by a simple declarator-id". I am using GCC 7.3.0.Borries
@Borries It's hard to say without seeing your code and command line... Maybe are you dealing with pointers like here?Jonquil
Clang doesnt accept it seems? coliru.stacked-crooked.com/a/c5d3c0f90ed263c2Stele
@Stele Apparently, Coliru is using clang 5.0. Judging by this, it should support C++17 template argument deduction, but evidently id doesn't. If you try your very same example in wandbox using clang 7.0, it works flawlessly.Jonquil
@PaoloM Oh cool, glad to know its just a compiler version issue. Thanks for looking into this.Stele
While the class template argument deduction as introduced by C++17 works well with GCC 8.X onwards it leads to a bug in GCC 7.X when combining default template data types and constant overloading (cs.technion.ac.il/users/yechiel/c++-faq/const-overloading.html). Consider the following example: wandbox.org/permlink/YoFIUEYNXZ80D6UN Even though one might expect the last instance "WhatAboutMe" to be a constant object it calls the non-constant implementation of "Method". As a consequence e.g. operators are able to change members of this seemingly constant object.Bushmaster
This should be top answer - the top answer is outdated.Walther
In short: You need at least gcc 7.0+ (ideally 8.0+) or clang 7.0+.Matronage
C
32

You are not allowed to do that but you can do this

typedef Foo<> Fooo;

and then do

Fooo me;
Cincture answered 18/11, 2015 at 10:34 Comment(2)
is there any difference in this with a default type and: typedef Foo<float> Fooo;, without a default type?Quadrifid
The C++11-ish way would be to say using Fooo = Foo<>;Autotransformer
C
22

You can use the following:

Foo<> me;

And have int be your template argument. The angular brackets are necessary and cannot be omitted.

Colonist answered 12/3, 2013 at 22:53 Comment(4)
Makes sense and thank you but, as noted below, why do the type specifies have ot be present?Trisyllable
@user633658: Did you mean "type specifier"? I'm not sure I understandColonist
Anyway, concerning the reason behind the need for empty angular brackets, I can only make conjectures, and they're all about ruling out possible ambiguities with the usage of the template's name alone, but I have to confess that I do not know the exact reasonColonist
I strongly suspect the requirement for the <> is to enable the compiler's parser to determine that you are referring to a templated class called foo, rather than something else called foo.Ghastly
R
0

Somewhat different case and rather later but where a template function is involved. gcc 11.2 can't seem to compile this:

template <typename T = int>
struct Test {};
template<typename T> void foo(T& bar) {}
int main()
{
    Test t;
    foo<Test>(t);
}

but has no problem with

template <typename T = int>
struct Test {};
template<typename T> void foo(T& bar) {}
int main()
{
    Test t;
    foo<Test<>>(t);
}

Of course

template <typename T = int>
struct Test {};
template<typename T> void foo(T& bar) {}
int main()
{
    Test t;
    foo(t);
}

works - but sometimes you need to explicitly force the type. Is this a compiler bug?

Rugging answered 30/3, 2022 at 15:21 Comment(5)
Note that this depends on compiler version - your third sample doesn't work in C++14 but works in C++17. This appears to be down to C++ language spec, not compiler error. Specifically, if you're going to partially specify a template (e.g. foo<Test>(t)), you need to provide <> for every defaulted template - however just putting foo(t) works fine.Onomastic
The answer gives additional information, but it appears to be speculative or partially a question itself.Onomastic
The actual usage that provoked this "questionable answer" requires the template argument. In that case it's not a partial specialization. I think that's actually the same case here - it is fully specialized. I believe there's another reason why this doesn't compile (but thanks for your comment).Rugging
I threw in this queston: #71683640Rugging
If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. - From ReviewTenterhook
Z
-1

As per the C++17 Standard, template arguments are necessary to be passed.

But if you still want a way around this, you can use using keyword like this

template <typename T>
class Foo{
};
using IFoo=Foo<int>

Or, you can also use preprocessor like this

template <typename T>
class Foo{
};

#define IFoo Foo<int>

Quick Reminder

Preprocessors are bad for debugging.

Zeist answered 3/2, 2022 at 5:26 Comment(1)
This is plain worse than using IFoo=Foo<int>. Macro's ignore namespacesChemotaxis

© 2022 - 2024 — McMap. All rights reserved.