Type inference in explicit constructors - c++

I'm toying with some type safety ideas using the following code which converts between related units . . .
#include <cmath>
#include <limits>
template <typename T>
class Pascal
{
private:
T val;
public:
explicit Pascal(const T val_)
{
val = val_;
}
operator T() const
{
return val;
}
};
template <typename T>
class dbSPL {
private:
T val;
public:
explicit dbSPL(const Pascal<T> p)
{
auto infProtect = std::numeric_limits<T>::min();
val = 20.0 * std::log10( infProtect + p / 20e-6 );
}
operator T() const
{
return val;
}
};
I want to know if it is possible to infer the template type from the constructor argument type, rather than explicitly declaring the template parameters. For example auto p = Pascal(0.5) rather than typing auto p = Pascal<double>(0.5), which would then lead to the neater dbSPL(Pascal(0.5)) over the more verbose dbSPL<double>(Pascal<double>(0.5)).

Use a helper function instead:
template <typename T>
dbSPL<T> make_dbspl(T t)
{
return dbSPL<T>(Pascal<T>(t));
}
int main()
{
auto dbspl = make_dbspl(0.5);
}
DEMO

One approach to solving this would be to create factory functions that are templated over the parameter types, and then return the dimensionalized types

As others said use wrapper function for this.
What I would add is that this approach is used even in c++ standard library, e.g. std::make_pair or std::make_shared in c++11. Reason for this is that when you write declaration like T t(x); then T is a type name and for templates type name contains template parameters. Additionally if T constructor is template itself then this constructor template parameters would be infered from type of x no template parameter of T itself.

I want to know if it is possible to infer the template type from the constructor argument type, rather than explicitly declaring the template parameters
No.
Neither the syntax nor the semantics of the language do or can support this.
Why not infer template parameter from constructor?
https://stackoverflow.com/a/6971914/560648
https://stackoverflow.com/a/7921464/560648
https://stackoverflow.com/a/29677772/560648

Related

c++ class method auto template parameter automatic deduction

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));
}

Overload operator [] with a template [duplicate]

In C++, can you have a templated operator on a class? Like so:
class MyClass {
public:
template<class T>
T operator()() { /* return some T */ };
}
This actually seems to compile just fine, but the confusion comes in how one would use it:
MyClass c;
int i = c<int>(); // This doesn't work
int i = (int)c(); // Neither does this*
The fact that it compiles at all suggests to me that it's doable, I'm just at a loss for how to use it! Any suggestions, or is this method of use a non-starter?
You need to specify T.
int i = c.operator()<int>();
Unfortunately, you can't use the function call syntax directly in this case.
Edit: Oh, and you're missing public: at the beginning of the class definition.
You're basically right. It is legal to define templated operators, but they can't be called directly with explicit template arguments.
If you have this operator:
template <typename T>
T operator()();
as in your example, it can only be called like this:
int i = c.operator()<int>();
Of course, if the template argument could be deduced from the arguments, you could still call it the normal way:
template <typename T>
T operator()(T value);
c(42); // would call operator()<int>
An alternative could be to make the argument a reference, and store the output there, instead of returning it:
template <typename T>
void operator()(T& value);
So instead of this:
int r = c.operator()<int>();
you could do
int r;
c(r);
Or perhaps you should just define a simple get<T>() function instead of using the operator.
Aren't you thinking of
class Foo {
public:
template<typename T>
operator T() const { return T(42); }
};
Foo foo;
int i = (int) foo; // less evil: static_cast<int>(foo);
live example. This proves you do not need to specify the template argument, despite the claim in the accepted answer.

Template parameter can't be deduced on implicitly constructed argument

I would like to have the following code in c++17:
#include <iostream>
#include <string>
#include <type_traits>
#include <functional>
class Foo;
template<class T>
class Bar {
public:
std::function<T(Foo&)> m_fn;
template<class Fn>
Bar(Fn fn) : m_fn(fn) {};
T thing(Foo &foo) const {
return m_fn(foo);
}
};
template<class Fn>
Bar(Fn) -> Bar<decltype(std::invoke(std::declval<Fn>(),
std::declval<Foo&>()))>;
class Foo {
public:
Foo() {};
template<class T>
std::vector<T> do_thing(const Bar<T> &b) {
std::vector<T> r;
r.push_back(b.thing(*this));
return r;
}
};
std::string test(Foo &) {
return "hello";
}
int main() {
Foo foo = Foo();
// works
std::vector<std::string> s = foo.do_thing(Bar{test});
// cant deduce T parameter to do_thing
std::vector<std::string> s = foo.do_thing({test});
}
But compiling this gives me "couldn't deduce template parameter ‘T’" on the call to do_thing.
Having do_thing(Bar{test}) fixes this and works fine but equates to some ugly code in the real code equivalent. I would like to have do_thing({test}) or do_thing(test) implicitly construct a Bar and pass that as the argument if possible.
I also don't want to forward declare a variable to pass into do_thing either
Is there some way to guide the inference of template argument T so that the call to do_thing can stay clean?
Edit:
Sorry for the late edit, but the arguments to the Bar constructor are over simplified in the example I included. In reality, there is an extra parameter std::optional<std::string> desc = std::nullopt and that might change in the future (although unlikely). So constructing the Bar inside do_thing would be a bit hard to maintain...
would like to have do_thing({test}) or do_thing(test) implicitly construct a Bar and pass that as the argument if possible.
Unfortunately, when you call do_thing({test}) or do_thing(test), test (or {test}) isn't a Bar<T> object. So the compiler can't deduce the T type and can't construct a Bar<T> object.
A sort of chicken-and-egg problem.
The best I can imagine is to add, in Foo, a do_test() method as follows
template<typename T>
auto do_thing (T const & t)
{ return do_thing(Bar{t}); }
This way you can call (without graphs)
std::vector<std::string> s = foo.do_thing(test);
You get the same result as
std::vector<std::string> s = foo.do_thing(Bar{test});
-- EDIT --
The OP ask
is there any way of preserving the {test} brace syntax? maybe with initializer_list or something?
Yes... with std::initializer_list
template<typename T>
auto do_thing (std::initializer_list<T> const & l)
{ return do_thing(Bar{*(l.begin())}); }
but, this way, you accept also
std::vector<std::string> s = foo.do_thing(Bar{test1, test2, test3});
using only test1
Maybe a little better... another way can be through a C-style array
template <typename T>
auto do_thing (T const (&arr)[1])
{ return do_thing(arr[0]); }
This way you accept only an element.
This happens because {} is not an expression and can only be used in limited ways while doing argument deduction, the parameter must have specific forms in order to succeed.
The allowed parameters types that can be used to deduce template parameters when {} is involved are better expanded in [temp.deduct.call]/1, two of the examples extracted from the cited part of the standard are:
template<class T> void f(std::initializer_list<T>);
f({1,2,3}); // T deduced to int
template<class T, int N> void h(T const(&)[N]);
h({1,2,3}); // T deduced to int
In your example the deduction guide is not used to deduce the T for {test} for the same as above.
foo.do_thing(Bar{test});
is your direct option without using additional functions.

Class template parameter inference with constructor

I have the following template class :
template <typename T>
struct timer
{
T period;
timer(T p) :
period(p)
{}
};
To instantiate it I need to do :
timer<double> t(double(0.0));
Is is possible to improve timer's class definition to allow this syntax :
timer t(double(0.0));
and have the compiler infer the double type from the constructor's argument ?
No, you can't do that. Type inference doesn't occur in those situations. You could use the auto keyword and a function template to make things easier though:
template<typename T>
timer<T> make_timer(T value) {
return value;
}
// let the compiler deduce double
auto t = make_timer(0.0);
Note that this use of the auto keyword is only valid in the C++11 standard.
Moreover, for this specific situation, you could typedef a double timer:
typedef timer<double> timer_d;
timer_d t(0.0);
Though I'd still go with the first solution, if you're able to use C++11.
No, it's not possible, deduction only works in functions. The usual solution is to write a make_ function which returns a new instance. This is C++11:
template <typename T>
timer<T> make_timer(T&& p) {
return timer<T>(std::forward<T>(p));
}
auto t = make_timer(0.0);

Function template with an operator

In C++, can you have a templated operator on a class? Like so:
class MyClass {
public:
template<class T>
T operator()() { /* return some T */ };
}
This actually seems to compile just fine, but the confusion comes in how one would use it:
MyClass c;
int i = c<int>(); // This doesn't work
int i = (int)c(); // Neither does this*
The fact that it compiles at all suggests to me that it's doable, I'm just at a loss for how to use it! Any suggestions, or is this method of use a non-starter?
You need to specify T.
int i = c.operator()<int>();
Unfortunately, you can't use the function call syntax directly in this case.
Edit: Oh, and you're missing public: at the beginning of the class definition.
You're basically right. It is legal to define templated operators, but they can't be called directly with explicit template arguments.
If you have this operator:
template <typename T>
T operator()();
as in your example, it can only be called like this:
int i = c.operator()<int>();
Of course, if the template argument could be deduced from the arguments, you could still call it the normal way:
template <typename T>
T operator()(T value);
c(42); // would call operator()<int>
An alternative could be to make the argument a reference, and store the output there, instead of returning it:
template <typename T>
void operator()(T& value);
So instead of this:
int r = c.operator()<int>();
you could do
int r;
c(r);
Or perhaps you should just define a simple get<T>() function instead of using the operator.
Aren't you thinking of
class Foo {
public:
template<typename T>
operator T() const { return T(42); }
};
Foo foo;
int i = (int) foo; // less evil: static_cast<int>(foo);
live example. This proves you do not need to specify the template argument, despite the claim in the accepted answer.