No deduction in a class template - c++

template<typename T>
class A {
public:
A(T b) : a(b) {
}
private:
T a;
};
A object(12); //Why does it give an error?
Why can't the type T be deduced from the argument 12 automatically?

Template argument deduction applies only to function and member function templates but not to class templates. So your code is ill-formed.
You need to provide the template argument explicitly.
A<int> object(12); //fine

Related

Deduce template parameter of class member from constructor of class

I have a class A which contains a templated member B whose exact type should be deduced from A's constructor. The way this is supposed to work is that, as shown in the below example, B can be instantiated with either 1 or 2 parameters to its constructor (deduction guide will tell) but will take a const char* in any case. When I instantiate A with the const char* argument for the constructor, an object B should be instantiated from the const char* as A only takes a B object. However, this is how far I get:
#include <iostream>
template <bool LengthOpt>
struct B
{
B(const char*) { }
B(const char*, size_t) { }
void print() {
if constexpr (LengthOpt) {
std::cout << "LengthOpt is set" << std::endl;
}
}
};
B(const char*) -> B<false>;
B(const char*, size_t) -> B<true>;
template <template <bool LengthOpt> class T>
struct A
{
A(T is) : is_{is} {
}
void print() {
is_.print();
}
T is_;
};
int main()
{
A a("hello");
a.print();
}
And it yields those errors:
<source>:24:7: error: use of template template parameter 'T' requires template arguments; argument deduction not allowed in function prototype
A(T is) : is_{is} {
^
<source>:21:43: note: template is declared here
template <template <bool LengthOpt> class T>
^
<source>:32:5: error: use of template template parameter 'T' requires template arguments; argument deduction not allowed in non-static struct member
T is_;
^
<source>:21:43: note: template is declared here
template <template <bool LengthOpt> class T>
^
<source>:37:7: error: no viable constructor or deduction guide for deduction of template arguments of 'A'
A a("hello");
^
<source>:22:8: note: candidate template ignored: could not match 'A<T>' against 'const char *'
struct A
^
<source>:22:8: note: candidate function template not viable: requires 0 arguments, but 1 was provided
My take on the problem is that the compiler doesn't know that I want to instantiate an object B in A's constructor, as the template template argument specifies nothing. It could very well be just any object that takes one template parameter.
I'm scratching my head right now on how to resolve this. Is it even possible or am I scratching a limitation in C++ again?

Template arguments can't be deduced for shared_ptr of class derived from templated base

I'm running into a case where I thought that the compiler would obviously be able to do template argument deduction, but apparently can't. I'd like to know why I have to give explicit template args in this case. Here's a simplified version of what's going on.
I have an inheritance hierarchy where the base class is templated and derived classes are concrete implementations of the base:
template <typename T> class Base {};
class Derived : public Base<int> {};
Then I have a templated function which accepts a shared pointer of the base class:
template <typename T> void DoSomething(const std::shared_ptr<Base<T>> &ptr);
When I try to call this method, I have to explicitly provide the template arguments:
std::shared_ptr<Derived> d = std::make_shared<Derived>();
DoSomething(d); // doesn't compile!
DoSomething<int>(d); // works just fine
In particular, I get this error message if I don't use explicit template arguments:
main.cpp:23:18: error: no matching function for call to ‘DoSomething(std::shared_ptr&)’
23 | DoSomething(d);
| ^
main.cpp:10:28: note: candidate: ‘template void DoSomething(const std::shared_ptr >&)’
10 | template <typename T> void DoSomething(const shared_ptr<Base<T>> &ptr)
| ^~~~~~~~~~~
main.cpp:10:28: note: template argument deduction/substitution failed:
main.cpp:23:18: note: mismatched types ‘Base’ and ‘Derived’
23 | DoSomething(d);
| ^
What's even more confusing to me is that template arguments can be deduced if I don't use shared_ptr:
template <typename T> void DoSomethingElse(const Base<T> &b);
Derived d;
DoSomethingElse(d); // doesn't need explicit template args!
So obviously I need to specify the template arguments for DoSomething. But my question is, why? Why can't the compiler deduce the template in this case? Derived implements Base<int> and the type can be deduced in DoSomethingElse, so why does sticking it in a shared_ptr change the compiler's ability to figure out that T should be int?
Full example code which reproduces the issue:
#include <iostream>
#include <memory>
template <typename T> class Base {};
class Derived : public Base<int> {};
template <typename T> void DoSomething(const std::shared_ptr<Base<T>> &ptr)
{
std::cout << "doing something" << std::endl;
}
template <typename T> void DoSomethingElse(const Base<T> &b)
{
std::cout << "doing something else" << std::endl;
}
int main()
{
std::shared_ptr<Derived> d = std::make_shared<Derived>();
// DoSomething(d); // doesn't compile!
DoSomething<int>(d); // works just fine
Derived d2;
DoSomethingElse(d2); // doesn't need explicit template args!
return 0;
}
Derived is a derived class of Base<int>, but std::shared_ptr<Derived> isn't a derived class of std::shared_ptr<Base<int>>.
So if you have a function of the form
template <typename T> void f(const Base<T>&);
and you pass a Derived value to it, the compiler will first notice that it can't match up Base<T> against Derived, and then try to match up Base<T> against a base class of Derived. This then succeeds since Base<int> is one of the base classes.
If you have a function of the form
template <typename T> void f(const std::shared_ptr<Base<T>>&);
and you pass a std::shared_ptr<Derived>, then the compiler will fail to match that against std::shared_ptr<Base<T>> and then try with base classes of std::shared_ptr<Derived>. If the latter has any base classes at all, they are internal to the standard library, and not related to std::shared_ptr<Base<T>>, so deduction ultimately fails.
What you are asking the compiler to do here is to say: "aha! std::shared_ptr<Derived> can be converted into std::shared_ptr<Base<int>>, which matches the function parameter!" But the compiler won't do that, because in general, there is no algorithm that the compiler can use in order to make a list of all types that a given type can be converted to.
Instead, you must help the compiler by telling it explicitly what to convert to. This can be done like so:
template <typename T>
Base<T> get_base(const Base<T>*); // doesn't need definition
template <typename T> void DoSomething(const std::shared_ptr<Base<T>> &ptr);
template <typename D, typename B = decltype(get_base((D*)nullptr))>
void DoSomething(const std::shared_ptr<D>& ptr) {
DoSomething(static_cast<std::shared_ptr<B>>(ptr));
}
Here, when the second DoSomething overload is called with an argument of type std::shared_ptr<D>, the get_base helper function will be used to determine the base class of D itself that has the form Base<T>. Then, the std::shared_ptr<D> will be explicitly converted to std::shared_ptr<Base<T>> so that the first overload can be called. Finally, note that if D isn't a derived class of any Base<T>, the second overload will be removed from the overload set, potentially enabling some other overload to handle the argument type.

How to force class template argument deduction when constructing a class in its own member functions?

Consider following code:
struct A {};
template <typename T> struct B
{
B(T) {}
auto foo() {return B(A{});} // error: no matching function for call to 'B<int>::B(A)'
};
auto foo() {return B(A{});} // compiles
int main()
{
foo();
B b(0);
b.foo();
}
Try it live
I understand why B::foo() doesn't compile: Inside of struct B<T>, B (as an injected-class-name) means B<T> unless it's explicitly used as a template. Which in this case prevents class template argument deduction.
Let's say I can't do auto foo() {return B<A>(A{});} since my actual code relies on slightly elaborate user-provided deduction guides.
The question is: How do I force class template argument deduction when constructing B inside of B::foo?
I hope I'm not missing something obvious.
You qualify it so that it's not the injected-class-name.
auto foo() {return ::B(A{});}
Another option is to use a function to do the type deduction for you.
template <typename T> B<T> make_b(T t) { return B<T>(t); }
and use
auto foo() {return make_b(A{});}

Default template parameters: Why does the compiler complain about not specifying template argument?

I have this code:
struct A{};
template<class T = A>
struct B {
void foo() {}
};
B b; //Error: missing template arguments before 'b'
//Error: expected ';' before 'b'
//More errors
b.foo()
If I make foo() as a template function with the same template 'signature', the compiler doesn't complain about not specifying the template arguments:
struct A {};
struct B {
template<class T = A>
void foo() {}
};
B b; //OK
b.foo()
So why do I need to specify an argument for a template class with a default parameter, but not for a template function? Is there some subtlety I am missing?
The reason is because of template argument deduction failure for sure. But I want to know why.
The correct syntax is this (demo):
B<> b;
The default argument A is assumed for the class template B. The <> part tells the compiler that B is a class template and asks it to take the default parameter as the template argument to it.
The correct syntax, as Nawaz mentions already is:
B<> b;
The reason is that B is the template and B<> is the instantiation of the template with the default argument A. But you need the <> to differentiate when you want an instantiation.
Because you have to say that B is a template:
B<> b;
Even when you don't want to specify any of the arguments.

Need help regarding Template class instantiation

Let me present my problem with an example :
template <typename T> class a{
public:
T data;
a():data(T()){}
a(T temp): data(temp) {}
};
So if write in main() like
a(30);
a("String");
So according to the template argument deduction rule , it should be able to generate the first temporary class as a<int>(30) etc
But I the error which says:
missing template arguments before '(' token
so why this happens, this is only true for function template?
Template parameter deduction from arguments only works for functions, never for classes. Until you know the type of the class, i.e. all its template parameters, you don't even know which member functions the class has!
So, you always have to say the template parameters if you want to construct an object directly:
a<int> x(30);
Here's a little thought experiment to expand on the above. Suppose we have
template <typename T> class Foo;
and we are calling Foo::somefunction(x);, where x is some type. You think, well, I declared somefunction() like this:
template <typename T> class Foo
{
static void somefunction(const T & x);
};
so it should be obvious that T is the same type as the type of x. But now imagine I have a specialization:
template <> class Foo<char>
{
static void anotherfunction(double x);
};
The class Foo<char> doesn't even have a function somefunction(), so the expression Foo::somefunction(x) doesn't even get to the stage where I could look up the argument!
The usual way around this is to make a free helper function that constructs your object:
template <typename T> a<T> make_a(const T & x) { return a<T>(x); }
Since this is a function template, its parameters can be deduced:
make_a(30); // type a<int>
make_a("hello"); // type a<char[6]>
The constructor is not a template, its the class which is a template. So when you write a(30), the template argument deduction for the class template cannot be done!
If there exists a constructor template, then the template argument for the templated constructor can be deduced by the compiler. For example here:
template <typename T> class A{
public:
template<typename U>
A(const U &): {} //note : it's a constructor template
};
A<char> obj(30); //U is deduced as int
In the above example, only U can be deduced, you still have to provide T. Its because
U is a template argument for the constructor template. Template argument deduction can be done in this case.
T is a template argument for the class template. Template argument deduction cannot be done here.
You still need to declare a temporary as, for example, a<int>(30).
You cannot infer class template arguments from the arguments to the constructor- unfortunately.
Template type deduction only happens for template functions. You need to specify the parameters for a template class instantiation . You can use a function template to deduce the template parameter and return the appropriate type. In c++0 x you could use auto to hold the instance. Can't easily write example code for you on my phone!