I understand in C++17, the following syntax is allowed (omitting the argument list).
template<typename T = int>
struct foo {
using type = T;
};
int main() {
foo f1; // compiles
foo<> f2; // compiles
}
Why is the following not supported? Why can I omit the argument list when declaring an instance, but not when accessing a member type?
int main() {
foo::type t1; // fails to compile
foo<>::type t2; // compiles
}
The name foo is the name of a template, not the name of a type. A template is a grammatical construct which generates a variable, function, or type, based on a set of arguments and some prototype (the template definition). That is, a template can generate a type, but the template itself is not a type.
foo is a template. foo<int> is a type, generated from applying int to the parameters of the template foo. foo<> is also a type, applying no arguments to the parameters of the template foo. But foo is just the name of a template.
However, there are certain places where the name of a template for a type can be used in place of a type name. For example, if you're in the definition of foo and refer to foo (such as specifying the name of the constructor or a function parameter or the like), you are referring to the specific foo being instantiated with a specific set of template parameters.
One other place where foo can be used in place of a type name is when creating an object (such as with foo f1;. This uses a process called class template argument deduction, which takes the arguments passed to the constructor and applies them through various processes to figure out what the template arguments should be. foo f1; works because that grammar triggers CTAD, and no arguments given means no template arguments are deduced, but foo's parameters are all defaulted, so that's fine.
foo::type is not grammar which triggers CTAD. Since CTAD is based on arguments to constructors (even if you don't pass any), CTAD only applies to grammar which creates an object from that template. There is no object being created, so CTAD doesn't apply. So foo just names a template, and a template can't have :: applied to it.
Related
Consider the following MCVE
struct A {};
template<class T>
void test(T, T) {
}
template<class T>
class Wrapper {
using type = typename T::type;
};
template<class T>
void test(Wrapper<T>, Wrapper<T>) {
}
int main() {
A a, b;
test(a, b); // works
test<A>(a, b); // doesn't work
return 0;
}
Here test(a, b); works and test<A>(a, b); fails with:
<source>:11:30: error: no type named 'type' in 'A'
using type = typename T::type;
~~~~~~~~~~~~^~~~
<source>:23:13: note: in instantiation of template class 'Wrap<A>' requested here
test<A>(a, b); // doesn't work
^
<source>:23:5: note: while substituting deduced template arguments into function template 'test' [with T = A]
test<A>(a, b); // doesn't work
LIVE DEMO
Question: Why is that? Shouldn't SFINAE work during substitution? Yet here it seems to work during deduction only.
Self introduction
Hello everyone, I am an innocent compiler.
The first call
test(a, b); // works
In this call, the argument type is A. Let me first consider the first overload:
template <class T>
void test(T, T);
Easy. T = A.
Now consider the second:
template <class T>
void test(Wrapper<T>, Wrapper<T>);
Hmm ... what? Wrapper<T> for A? I have to instantiate Wrapper<T> for every possible type T in the world just to make sure that a parameter of type Wrapper<T>, which might be specialized, can't be initialized with an argument of type A? Well ... I don't think I'm going to do that ...
Hence I will not instantiate any Wrapper<T>. I will choose the first overload.
The second call
test<A>(a, b); // doesn't work
test<A>? Aha, I don't have to do deduction. Let me just check the two overloads.
template <class T>
void test(T, T);
T = A. Now substitute — the signature is (A, A). Perfect.
template <class T>
void test(Wrapper<T>, Wrapper<T>);
T = A. Now subst ... Wait, I never instantiated Wrapper<A>? I can't substitute then. How can I know whether this would be a viable overload for the call? Well, I have to instantiate it first. (instantiating) Wait ...
using type = typename T::type;
A::type? Error!
Back to L. F.
Hello everyone, I am L. F. Let's review what the compiler has done.
Was the compiler innocent enough? Did he (she?) conform to the standard?
#YSC has pointed out that [temp.over]/1 says:
When a call to the name of a function or function template is written
(explicitly, or implicitly using the operator notation), template
argument deduction ([temp.deduct]) and checking of any explicit
template arguments ([temp.arg]) are performed for each function
template to find the template argument values (if any) that can be
used with that function template to instantiate a function template
specialization that can be invoked with the call arguments. For each
function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to
synthesize the declaration of a single function template
specialization which is added to the candidate functions set to be
used in overload resolution. If, for a given function template,
argument deduction fails or the synthesized function template
specialization would be ill-formed, no such function is added to the
set of candidate functions for that template. The complete set of
candidate functions includes all the synthesized declarations and all
of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the
remainder of overload resolution, except as explicitly noted in
[over.match.best].
The missing type leads to a hard error. Read https://stackoverflow.com/a/15261234. Basically, we have two stages when determining whether template<class T> void test(Wrapper<T>, Wrapper<T>) is the desired overload:
Instantiation. In this case, we (fully) instantiate Wrapper<A>. In this stage, using type = typename T::type; is problematic because A::type is nonexistent. Problems that occur in this stage are hard errors.
Substitution. Since the first stage already fails, this stage is not even reached in this case. Problems that occur in this stage are subject to SFINAE.
So yeah, the innocent compiler has done the right thing.
I'm not a language lawyer but I don't think that defining a using type = typename T::type; inside a class is, itself, usable as SFINAE to enable/disable a function receiving an object of that class.
If you want a solution, you can apply SFINAE to the Wrapper version as follows
template<class T>
auto test(Wrapper<T>, Wrapper<T>)
-> decltype( T::type, void() )
{ }
This way, this test() function is enabled only for T types with a type type defined inside it.
In your version, is enabled for every T type but gives error when T is incompatible with Wrapper.
-- EDIT --
The OP precises and asks
My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper itself can be instantiated?
As suggested by Holt, you can create a custom type traits to see if a type is a Wrapper<something> type; by example
template <typename>
struct is_wrapper : public std::false_type
{ };
template <typename T>
struct is_wrapper<Wrapper<T>> : public std::true_type
{ using type = T; };
Then you can modify the Wrapper version to receive a U type and check if U is a Wrapper<something> type
template <typename U>
std::enable_if_t<is_wrapper<U>{}> test (U, U)
{ using T = typename is_wrapper<U>::type; }
Observe that you can recover the original T type (if you need it) using the type definition inside the is_wrapper struct.
If you need a non-Wrapper version of test(), with this solution you have to explicity disable it when T is a Wrapper<something> type to avoid collision
template <typename T>
std::enable_if_t<!is_wrapper<T>{}> test(T, T)
{ }
Deduction of the function called in a function call expression is performed in two steps:
Determination of the set of viable functions;
Determination of the best viable function.
The set of viable function can only contain function declaration and template function specialization declaration.
So when a call expression (test(a,b) or test<A>(a,b)) names a template function, it is necessary to determine all template arguments: this is called template argument deduction. This is performed in three steps [temp.deduct]:
Subsitution of explicitly provided template arguments (in names<A>(x,y) A is explicitly provided);(substitution means that in the function template delcaration, the template parameter are replaced by their argument)
Deduction of template arguments that are not provided;
Substitution of deduced template argument.
Call expression test(a,b)
There are no explictly provided template argument.
T is deduced to A for the first template function, deduction fails for the second template function [temp.deduct.type]/8. So the second template function will not participate to overload resolution
A is subsituted in the declaration of the first template function. The subsitution succeeds.
So there is only one overload in the set and it is selected by overload resolution.
Call expression test<A>(a,b)
(Edit after the pertinent remarks of #T.C. and #geza)
The template argument is provided: A and it is substituted in the declaration of the two template functions. This substitution only involves the instantiation of the declaration of the function template specialization. So it is fine for the two template
No deduction of template argument
No substitution of deduced template argument.
So the two template specializations, test<A>(A,A) and test<A>(Wrapper<A>,Wrapper<A>), participate in overload resolution. First the compiler must determine which function are viable. To do that the compiler needs to find an implicit conversion sequence that converts the function argument to the function parameter type [over.match.viable]/4:
Third, for F to be a viable function, there shall exist for each argument an implicit conversion sequence that converts that argument to the corresponding parameter of F.
For the second overload, in order to find a conversion to Wrapper<A> the compiler needs the definition of this class. So it (implicitly) instantiates it. This is this instantiation that causes the observed error generated by compilers.
Consider a simple example:
int x;
template <template <auto> class TT>
struct Foo {
void foo() {
TT<(x)> tt;
static_cast<void>(tt);
}
};
template <decltype(auto)>
struct Bar { };
int main() {
Foo<Bar> foobar;
foobar.foo();
}
[clang] seems to deal with the idea of decltype(auto) placeholder despite the use of auto in template template parameter declaration without a problem.
[gcc] on the other hand - not very well:
prog.cc:6:13: error: the value of 'x' is not usable in a constant expression
As usually - which behaviour is expected according to standard? Or maybe everything is possible and the code is ill-formed (this time I suppose not but cannot rule it out definitively)?
PS. Sorry for breaking one of the compilers again ;)
The original answer here had Foo<Bar> ill-formed, I actually now think it's well-formed. But ultimately, clang bug based.
I actually think even Foo<Bar> is ill-formed. The new rules, following P0522 are that:
A template-argument matches a template template-parameter P when P is at least as specialized as the template-argument A
where:
A template template-parameter P is at least as specialized as a template template-argument A if, given the following rewrite to two function templates, the function template corresponding to P is at least as specialized as the function template corresponding to A according to the partial ordering rules for function templates ([temp.func.order]). Given an invented class template X with the template parameter list of A (including default arguments):
Each of the two function templates has the same template parameters, respectively, as P or A.
Each function template has a single function parameter whose type is a specialization of X with template arguments corresponding to the template parameters from the respective function template where, for each template parameter PP in the template parameter list of the function template, a corresponding template argument AA is formed. If PP declares a parameter pack, then AA is the pack expansion PP... ([temp.variadic]); otherwise, AA is the id-expression PP.
If the rewrite produces an invalid type, then P is not at least as specialized as A.
Which means that to verify if Foo<Bar> itself is okay, we synthesize:
template <decltype(auto) I> struct X;
template <auto I> void __f(X<I> ); // P
template <decltype(auto) I> void __f(X<I> ); // A
All the types here are valid (so the last statement doesn't apply). Now, typically when we do partial ordering it's in the context of either overload resolution or picking a class template specialization, in which case what we're looking for is the "more specialized" function template, where F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.
But in this context, we don't care about which is more specialized. We only need P to be at least as specialized as A. All that means that deduction has to succeed from A to P. So if we synthesize some unique type U with some value V, can we deduce X<I> from X<V>? Yes. Hence, P is at least as specialized as A, so the template-argument Bar matches the template-parameter TT.
Now, passing that point, I'd say this a clang bug. The template template-parameter is template <auto>, which is what we should use to validate the expression. With a non-type template parameter auto, we'd try to use x as a value - but x isn't a valid constant expression, so this should fail. clang appears to be using template <decltype(auto) > directly - which I'm not sure is valid.
That said, I'm not sure this case has even been considered - I don't see any wording one way or the other and it's worth raising an issue.
So I have the following bit of code:
template <typename Type>
class Delegate
{
public:
Delegate(Type x)
{
}
};
void Method()
{
}
int main()
{
Delegate d(&Method);
return 0;
}
My question is: why can't the compiler deduce the template type based on what's passed into the constructor? The compile error I get is: Argument list for class template Delegate is missing. I understand that, but I thought type inference could overcome this to allow for cleaner syntax.
Because template parameter deduction only applies to functions. Class templates require parameters explicitly, always.
That's why many templates have a "named constructor" a function that simply constructs a temporary instance, but by virtue of being a function template rather than class template deduces parameters. For example std::make_pair.
C++11 introduced this new meaning of auto that actually allows you to deduce type of variable. So if you have C++11, you can create a "named constructor" for your class, like:
template <typename Type>
Delegate<Type> delegate(Type x) { return Delegate<Type>(x); }
and you can create a variable of deduced type with it like:
auto d = delegate(&Method);
Note, that this deduces d to be exactly the type returned by the initializer (you can have auto & or auto && if you want, but not much beyond that). This is way easier than trying to deduce hypothetical Delegate d(&Method), because that would involve cyclical dependency between deducing the type depending on overload resolution between constructors and the set of viable constructors depending on the deduced type (remember, constructors can be overloaded and types can be partially specialized).
It's for the same reason that this won't work:
// attempt to create a std::vector<std::string> of ten "x" strings:
std::vector v(10, "x");
In fact, it should result in the very same error message.
Use something like this to use type deduction:
template <class Type>
Delegate<Type> MakeDelegate(Type const &x)
{
return Delegate<Type>(x);
}
Or just do as you'd do with a std::vector and declare the type explictly.
By the way, main must return int, and arguments of unknown type (i.e. in templates) should be passed with const&.
I've seen code like this:
#include <iostream>
// member_function_templates.cpp
struct X
{
template <class T> void mf(T* t) {
std::cout << "in mf t is: " << *t << std::endl;
}
};
int main()
{
int i = 5;
X* x = new X();
x->mf(&i); //why can this be called without specifying a type as in: x->mf<int>(&i) ??
}
My question is in the comment there in main. Why can you call:
x->mf(&i);
...without specifying a type? Intuition says it should be called like:
x->mf<int>(&i);
... but apparently does not have to be called like this (the above compiles with gcc 4.7 for me either way - with of without explicitly specifying the template.)
And, if you call it without specifying a type for the template, what will the type of T be (in the template function definition)? (I'm guessing it defaults to the type of whatever it is you pass into the function mf as an argument, but further explanation of how this works would be nice)
That is template type deduction and it applies to all template functions, not just members of a non-templated class. Basically the compiler is able to infer (following a set of rules in the standard) what the type T means from the arguments to the function. When the compiler sees the call to mf(&i);, it knows that i is an int, so the argument is an int* and that means that you want the overload that has T==int (*)
You can still provide a particular set of template arguments if you want to force a particular specialization of the template. If, for example, you want the parameter to the template not be the deduced one, but one that is convertible from it:
template<typename T>
void foo(T arg) {...}
int i = 10;
foo(i); // calls foo<int>(i)
foo<double>(i); // calls foo<double>(static_cast<double>(i))
(*) It is slightly more complicated than this, as there could be potentially multiple T types for which the argument would be valid, but the rules in the language determine what particular T will be deduced. In this case, it is int.
I find something annoying in C++ and I don't know if there is a trick to avoid this with no overhead. The problem is the following :
For a template function, we can have :
// Function declaration/definition
template<bool Option = false> void myFunction()
{
std::cout<<"Option = "<<Option<<std::endl;
}
// Then I can use :
myFunction<false>();
myFunction<true>();
myFunction(); // <- NO PROBLEM HERE
Now for a template class :
// Class definition/declaration
template<bool Option = false> class MyClass
{
};
// Then I can use :
myClass<false> x;
myClass<true> y;
myClass z; // <- PROBLEM HERE : only "MyClass<> z;" will compile !
Why is the reason of this behaviour ?
Is there any trick to avoid that ?
For a class with optionnal parameters passed as template, I find this not convenient for the end user : he should be able to use the default implementation as a no-templated class...
Why is the reason of this behaviour ?
It's because functions can be overloaded, and types can't.
When you write a function call, the compiler populates an overload set of all the functions it can find with that name, and then figures out which ones match the argument(s) passed. Now, for this to work cleanly with function templates, it allows the template argument types to be deduced from the parameters. Because type parameter inference is allowed in general, it works for your case even when the parameter is defaulted instead.
Types, however, aren't overloaded. While myFunction<true>() and myFunction<false>() are both related to the extent they'll participate in the same overload set, myClass<true> and myClass<false> are separate and unrelated types. With no equivalent of overloading on type names, there's no motivation to add a special case for implicitly naming a fully-specialized template class. The parameters can never be inferred, so it would amount to special syntax only for the case where they're all defaulted.
Is there any trick to avoid that ?
In general, if you want to get template argument deduction for template classes, you can provide a template function wrapper (this works best with C++11 auto)
template <bool Option=false> class MyClass {};
template <bool Option=false> MyClass<Option> make_my_class() {
return MyClass<Option>();
}
// ...
auto z = make_my_class();
Otherwise, I think using typedef (as per Remy's comment) is the best option.
myClass is a class template, not a class. Only myClass<true>, or myClass<>, is a class.
Similarly, myFunction is a function template, not a function. However, when you're invoking a templated function, the template arguments may be deduced for you, and you don't need to specify template arguments explicitly if they can be deduced. Thus the function call expression myFunction(); is valid, and the first argument is deduced as false. It's just that the deduction happens thanks to the default argument rather than matching against function arguments.