c++ class method auto template parameter automatic deduction - c++

Is it possible for a class method to be called somehow and automatically deduce templated arguments?
I'm trying to create an object with a create class method that calls a private constructor.
I'm doing so in order to make sure the created instance is allocated on the heap.
The class in templated and I'd like the user not having to know the types (can be complicated with rvalue reference to lambdas).
Here's a toy example of what I'd like to achieve (I know the constructor is public here):
template <typename T>
class hello
{
public:
hello(T t){}
static std::shared_ptr<hello<T>> create(T t)
{
return std::make_shared<hello<T>>(t);
}
};
template <typename T>
auto create_hello(T t)
{
return std::make_shared<hello<T>>(t);
}
void test_hello()
{
auto h1 = hello(3); // ok
auto h2 = std::make_shared<hello<int>>(3); // ok
auto h3 = create_hello(3); // ok
auto h4 = hello<int>::create(3); // ok
auto h5 = hello::create(3); // Compile error: 'hello' is not a class, namespace, or enumeration
}
Is there a way to invoke the static create method and having the template parameters automatically deduced like when calling the constructor directly?

No, this is not currently possible. There are only a few contexts in which the template arguments of a class can be deduced:
any declaration that specifies initialization of a variable and variable template, e.g.
hello h(3);
new-expressions, e.g.
auto h = new hello{3};
function-style cast expressions, e.g.
auto h = hello(3);
and from c++20:
the type of a non-type template parameter
In this expression:
hello::create(3)
the template argument needs to be deduced, since it's not specified, but none of the above contexts apply, since there is no rule for deduction of a template parameter from the invocation of a static member function.

You are mixing up two different things:
Constructor template deduction and
function template deduction.
Whereas auto h1 = hello(3) works since, it makes use of constructor template deduction, but since create is not a constructor, the template parameter cannot be deduced from the parameter.
To solve that, you have to source out your create function:
template <class U>
static std::shared_ptr<hello<T>> make_hello(U t)
{
return std::make_shared<hello<U>>(t);
}
Edit:
If you want have the constructor private, just friend the function:
template <typename T>
class hello
{
hello(T t){}
public:
template <class U>
friend std::shared_ptr<hello<U>> make_hello(U t);
};
template <class T>
std::shared_ptr<hello<T>> make_hello(T t)
{
return std::shared_ptr<hello<T>>(new hello<T>(t));
}

Related

Nested template argument deduction

I have some function that is templated on output type. This function then accepts an input argument that in its turn is templated on the output type. I do not want to specify the output type twice as that just clutters the api. In my world, I have told the compiler everything it needs to know to deduce this correctly but I cannot get it to work. Suggestions?
template<typename T>
struct TestStruct {};
template<typename T, template<typename> class U>
T testFunc(U<T> arg)
{
return T{0};
}
int main()
{
testFunc<double>(TestStruct<double>{}); // Compiles
testFunc<double>(TestStruct{}); // Does not compile
}
The problem isn't with testFunc but with TestStruct{} as you're not passing a template argument and the corresponding template parameter of class template TestStruct doesn't have a default argument.
I do not want to specify the output type twice
As you want to specify the double only once you can do:
template<typename T, template<typename> class U>
T testFunc(U<T> arg)
{
return T{0};
}
int main()
{
//----------------------vvvvvv-------------->only once as you want
testFunc(TestStruct<double>{});
//----------^------------------------------->no need to specify double here as it can be deduced
}
Demo
As shown in the above modified program, we're only specifying double once when passing it as a template argument for TestStruct. The template argument for T of function template testFunc can be deduced.

Passing unique_ptr<Derived<T>> to a function

I need to pass a unique pointer to a derived template class to a function that takes a unique base template class, like this:
template <typename T>
class Base {};
template <typename T>
class Derived : public Base<T> {};
template <typename T>
void foo(std::unique_ptr<Base<T>>){}
//or
template <typename T>
class MyClass{
public:
MyClass(std::unique_ptr<Base<T>> arg) : _arg(std::move(arg)) {}
private:
std::unique_ptr<Base<T>> _arg;
};
int main()
{
auto b = make_unique<Derived<int>>();
foo(std::move(b));
MyClass mc(std::move(b))
}
Why is this not working and how can I fix it?
I get an error:
'void foo1<T>(std::unique_ptr<Base<T>,std::default_delete<Base<T>>>)': cannot convert argument 1 from 'std::unique_ptr<Derived<int>,std::default_delete<Derived<int>>>' to 'std::unique_ptr<Base<T>,std::default_delete<Base<T>>>'
but it work
auto derived = std::make_unique<Derived<int>>();
std::unique_ptr<Base<int>> base = std::move(derived);
C++ doesn't deduce template arguments in this situation. You can specify <int>, and that will succeed.
foo<int>(std::move(b)); // fine
MyClass<int> mc(std::move(b)); // fine
See it on coliru
You can't have template argument deduction also consider implicit conversions, at least not in most situations. Normally the argument type must match the parameter type exactly for deduction of a template argument to be possible (in this case to deduce T), but std::unique_ptr<Base<int>> and std::unique_ptr<Dervived<int>> are not the same type.
As the other answer suggests you can explicitly specify the template argument instead of trying to have it be deduced.
If you want to automate this without having to add anything to Derived or Base you can however make use of one of the exceptions to the general rule above. If the template parameter is a reference-to or pointer-to base of the argument type, then it may (with certain conditions) still be used for deduction:
// Here an exception to the deduction rules applies
// and `Base<T>*` can be deduced against a pointer `X*`
// if `X` is (uniquely) derived from a `Base<T>`
template<typename T>
auto as_base_ptr(Base<T>* p){
return p;
}
template<typename X>
auto to_base_unique_ptr(std::unique_ptr<X> p) {
using base_type = std::remove_pointer_t<decltype(as_base_ptr(std::declval<X*>()))>;
return std::unique_ptr<base_type>(std::move(p));
}
template <typename T>
void foo(std::unique_ptr<Base<T>>){
}
template <typename X>
void foo(std::unique_ptr<X> p){
foo(to_base_unqiue_ptr(std::move(p)));
}
But even simpler you can ask yourself whether you really need to have the function foo take std::unique_ptr<Base<T>> specifically (e.g. because you need access to T) or whether std::unique_ptr<X> wouldn't already be enough.

Calling lambda with non-parameter template argument

MSVC 2019 allow me to define a lambda like this, where the template argument is not used in the parameter list:
auto foo = []<bool B>() {
return B;
};
However, it gives syntax error when trying to call it like this?
foo<false>();
What is the correct way to call a lambda with a non-parameter template argument?
The template parameter is used with operator() of lambda.
(since C++20) If the lambda definition uses an explicit template parameter list, that template parameter list is used with operator().
You can specify the non-type template argument for operator(), call the lambda in an unnormal style as:
foo.operator()<false>();
foo is a function object. It has an operator(). The template on a lambda is on that operator, not on the object name itself.
foo<T> attempts to give the name foo a template argument T; that isn't valid here. foo<T>() gives the template argument to foo, then calls () on the result. Again, not what is going on here.
A template variable would work the way you want this to:
template<bool B>
auto foo = []() {
return B;
};
such variables only work in global scope, but here foo<true>() would work.
As a lambda is actually a compiler-generate class:
struct some_secret_name {
template<bool B>
auto operator()() const {
return B;
}
};
some_secret_name foo;
then the "right" way to call it is the ugly syntax:
foo.operator()<true>();
this, honestly, sucks.
You can get slightly better syntax like this:
template<class T> struct tag_t {using type=T;};
template<class T> constexpr tag_t<T> tag_v={};
template<auto v> using value_t = std::integral_constant<std::decay_t<decltype(v)>, v>;
template<auto v> constexpr value_t<v> value_v={};
then we can do
auto foo = []<bool B>(value_t<B>) {
return []{
return B;
};
};
now the syntax is:
foo(value_v<false>)();
which looks a bit less cludgy, if overly magical.
foo is now a lambda that makes lambdas, the outer lambda takes your template parameters as function arguments, and the inner lambda is the body.

Use derived class for template constructor

I have a class with a template constructor:
class TCons {
template <typename T> TCons(T t);
}
which is specialized in the implementation:
template <> TCons::TCons(int i) { doMyStuff(i); }
I also have a specialization for a base class:
template <> TCons::Tcons(TBase &t) { doMyStuff(t); }
But this doesn't seem to work when I try to initialize a TCons object using a derived object as a parameter.
class TDeriv: public TBase { };
TDeriv td;
TCons tc = td;
I can't use a pointer to resolve this issue (since everything is wrapped inside a macro). The problem arises during the link phase.
Is it just wrong, or am I missing something?
When we try to construct tc here:
TCons tc = td;
we have one choice of constructor:
template <typename T> TCons(T t);
When we perform template deduction, we deduce T = TDeriv. This does not match your TBase explicit specialization (nor the int one), so we stick with the primary template. You don't provide a definition for it, which is why you have a linker error.
If you want your TBase constructor to be called on all types that inherit from TBase, you'll have to disable the constructor template for those cases. We can do that with SFINAE:
template <typename T,
typename = std::enable_if_t<!std::is_base_of<TBase, T>::value>>
TCons(T );
while additionally making your other constructors non-template overloads:
TCons(int );
TCons(TBase& );
Only specialize when you need to - overloading is going to simpler.

C++ template constructor

I wish to have a non-template class with a template constructor with no arguments.
As far as I understand, it's impossible to have it (because it would conflict with the default constructor - am I right?), and the workaround is the following:
class A{
template <typename U> A(U* dummy) {
// Do something
}
};
Maybe there is a better alternative for this (or a better workaround)?
There is no way to explicitly specify the template arguments when calling a constructor template, so they have to be deduced through argument deduction. This is because if you say:
Foo<int> f = Foo<int>();
The <int> is the template argument list for the type Foo, not for its constructor. There's nowhere for the constructor template's argument list to go.
Even with your workaround you still have to pass an argument in order to call that constructor template. It's not at all clear what you are trying to achieve.
You could use a templated factory function instead of a constructor:
class Foo
{
public:
template <class T> static Foo* create() // could also return by value, or a smart pointer
{
return new Foo(...);
}
...
};
As far as I understand, it's impossible to have it (because it would conflict with the default constructor - am I right?)
You are wrong. It doesn't conflict in any way. You just can't call it ever.
template<class...>struct types{using type=types;};
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
the above helpers let you work with types as values.
class A {
template<class T>
A( tag<T> );
};
the tag<T> type is a variable with no state besides the type it caries. You can use this to pass a pure-type value into a template function and have the type be deduced by the template function:
auto a = A(tag<int>{});
You can pass in more than one type:
class A {
template<class T, class U, class V>
A( types<T,U,V> );
};
auto a = A(types<int,double,std::string>{});
Some points:
If you declare any
constructor(including a templated
one), the compiler will refrain from
declaring a default constructor.
Unless you declare a copy-constructor (for class X one
that takes X or X& or X const
&) the compiler will generate the
default copy-constructor.
If you provide a template constructor for class X which takes
T const & or T or T& then the
compiler will nevertheless generate a
default non-templated
copy-constructor, even though you may think that it shouldn't because when T = X the declaration matches the copy-constructor declaration.
In the latter case you may want to provide a non-templated copy-constructor along with the templated one. They will not conflict. When X is passed the nontemplated will be called. Otherwise the templated
HTH
You could do this:
class C
{
public:
template <typename T> C(T*);
};
template <typename T> T* UseType()
{
static_cast<T*>(nullptr);
}
Then to create an object of type C using int as the template parameter to the constructor:
C obj(UseType<int>());
Since you can't pass template parameters to a constructor, this solution essentially converts the template parameter to a regular parameter. Using the UseType<T>() function when calling the constructor makes it clear to someone looking at the code that the purpose of that parameter is to tell the constructor what type to use.
One use case for this would be if the constructor creates a derived class object and assigns it to a member variable that is a base class pointer. (The constructor needs to know which derived class to use, but the class itself doesn't need to be templated since the same base class pointer type is always used.)
Here's a workaround.
Make a template subclass B of A. Do the template-argument-independent part of the construction in A's constructor. Do the template-argument-dependent part in B's constructor.
It is perhaps easier and more intuitive to rely on std::in_place_type_t<T> which is used in std::variant, std::any, etc for exactly the same purpose:
#include <utility>
class A {
template <typename U>
A(std::in_place_type_t<U>) {
// Do something
}
};
A a(std::in_place_type_t<MyType>{});
try doing something like
template<class T, int i> class A{
A(){
A(this)
}
A( A<int, 1>* a){
//do something
}
A( A<float, 1>* a){
//do something
}
.
.
.
};
Just simple to add a dummy variable like
class A {
template<typename T>
A(const T&, int arg1, int arg2);
}