Darkdragon84
Darkdragon84

Reputation: 901

C++ template parameter deduction by assignment

I want something very simple: a template function that contains the template parameter just in the return type, but not in the function parameters. The template parameter should then be deduced from an assignment when calling the function.

To be specific, let's consider a function creating random matrices of type MatType<T> of a certain dimension. The template parameter T is the numeric type of the matrix (e.g. matrix of doubles, floats, ints, etc.):

template<typename T>
MatType<T>
randmat(int m, int n)
{ // create MatType<T> of size m x n, fill with random numbers and return it.}

Now for some reason template parameters cannot a priori be deduced from the return type, so when I use this function I have to call it as e.g.

MatType<double> M = randmat<double>(10,10);

I however would like a way around this, i.e. call the function just as

MatType<double> M = randmat(10,10);

i.e. have the function determine the template parameter from the return type MatType<double>. This is only partly because I am lazy, as the template parameter in my case can become long and nasty.

This might seem like a stupid question that many people have asked before, but I didn't really find any satisfactory answer other than saying that it's not possible. I am however using a matrix library called armadillo, which has template functions with exactly that ability, so I know it's possible (but somehow too complicated and time consuming for me to extract how it's done from the source code).

Can this be done with some traits or template meta-programming? Also how would one deal with function calls without assignment like randmat(10,10);?

Thanks

Upvotes: 2

Views: 898

Answers (4)

Ami Tavory
Ami Tavory

Reputation: 76297

What you're asking is not exactly possible, but some aspects of it might work "by chance" depending on the domain of the problem.

In the line

MatType<double> M = randmat(10,10);

it's not possible (in C++) to deduce the template parameter based on the type of M, because by the language rules, first the RHS is constructed, only then assignment (or copy construction as is the case here) happens. So at construction, the later operation doesn't have a say.

However, assume that randmat(10, 10) returns a matrix of doubles, and that MatType<T> has a copy constructor taking a const reference to MatType<U> (for some other type U). Then this gives you not exactly what you asked, but a similar bottom-line effect. This is what is happening in Armadillo, BTW.


Another - particularly elegant solution by @jxh - is to use lazy computation, and defer calculating anything until assignment to a concrete type.

I have to say I am not crazy about it. As you write in the question, you could perform an operation on the temporary returned object without any assignment, e.g.,

randmat(10,10).foo();

Consequently, you must commit in your code what the logical default specific returned type is. Given that you do so, and most algebraic/random-generation operations are far more complicated than a simple copy-conversion, it seems like a complicated overkill to me in this case.

Upvotes: 2

M.M
M.M

Reputation: 141618

A simple solution that avoids duplication is:

auto m = randmat<double>(10, 10);

If you really want to use your original proposed syntax, you will have to make randmat be a placeholder which just passes information through to a point at which you know the destination type. For example:

struct randmat
{
    int x, y; 
    randmat(int x, int y): x(x), y(y) {}
};

template<typename T>
struct MatType
{
   int x, y;

   MatType(randmat r): x(r.x), y(r.y)
   {
       // call your random generation code here; can use T
   }
};

int main()
{
   MatType<double> m = randmat(10, 10);
}

Upvotes: 1

jxh
jxh

Reputation: 70422

One way to achieve this effect more efficiently is to defer computation of the random values until the return type is known. You can achieve this with a proxy object whose only purpose is to detect the type being assigned to, and then perform the computation using a type appropriate object.

struct RandMatType {
    const int m_;
    const int n_;
    RandMatType (int m, int n) : m_(m), n_(n) {}
    template <typename T>
    operator MatType<T> () const {
        MatType<T> M;
        //... fill M
        return M;
    }
};

RandMatType randmat(int m, int n) {
   return RandMatType(m, n);
}

In the above code, randmat() first returns the proxy of type RandMatType. Then, during assignment, the proxy object is converted to target type. The conversion operator then assumes the conversion type is the type of matrix to use.

Upvotes: 2

Rumburak
Rumburak

Reputation: 3571

One option is to return MapType

MatType<int>
randmat(int m, int n)
{ // create MatType<int> of size m x n, fill with random numbers and return it.}

And have a conversion operator to generate the required type

template<typename T>
struct MatType
{
   template<typename T>
   operator MatType<T>()
   {...}
};

Upvotes: 0

Related Questions