NetherGranite
NetherGranite

Reputation: 2100

"No Matching Constructor" Error When Trying to Instantiate Object of a Template Class

Edit:

  1. I am so sorry about the this and the lack of a ; at the end of my template class; I had some issues copying and pasting my code, so I copied some code by hand and messed those parts up.
  2. After reopening the IDE, the error just magically vanished. Maybe Repl.it was having some issues. The error message is different now. If I am unable to solve this error on my own, I will ask a new question.

Thank you to HugoTeixeira, Matthew Fisher, and user4581301 for your thoughtful responses.


I have the following code in Group.h:

template <typename T, int N> class Group
{
    public:
        T values[N];

        Group(T args[])
        {
            for (int i = 0; i < N; i++)
            {
                values[i] = args[i];
            }
        }

        Group()
        {
            Group((T[]){0, 0, 0});
        }
};

and in main.cpp, I have this code:

#include "Group.h"

int main()
{
    Group<double, 3> * v1 = new Group<double, 3>();
}

When I try to run this code, my IDE gives me the error:

no matching constructor for initialization of 'Group<double, 3>'

I have tried writing this code but minus the template, and it worked fine. What am I doing wrong?

Upvotes: 1

Views: 317

Answers (3)

Matthew Fisher
Matthew Fisher

Reputation: 2336

It seems this is an edge cases for the compilers. Various flavors compile for one C++ compiler and not another. The code below works for me on gcc and clang.

template <typename T, int N> class Group
{
    public:
        T values[N];

        Group(T args[])
        {
            for (int i = 0; i < N; i++)
            {
                values[i] = args[i];
            }
        }

        Group()
        {
          Group((T[]){0, 0, 0});
        }
};

int main()
{
    Group<double, 3> * v1 = new Group<double, 3>();
}

Upvotes: 1

user4581301
user4581301

Reputation: 33932

I'm going to head in a different direction and ditch those array casts and other dodgy behavior for a std::initializer_list (documentation) and a delegated constructor.

template <typename T, size_t N>// replaced int with size_t. 
                               //why allow a negative size?
class Group
{
    public:
        T values[N];

        Group(std::initializer_list<T> list)  
        {
            // may be different sizes
            size_t copylen = std::min(list.size(), N); 
            std::copy(list.begin(), 
                      list.begin()+copylen, 
                       values); // copy all we can

            //default initialize remainder, if any
            for (size_t i = copylen; i < N; i++)
            { 
                values[i] = T();
            }
        }
        Group():Group{T()} // might be a cleaner way to do this. 
                           // I don't know it.
        {
        }
/* this may be more efficient. A good compiler can optimize this 
   down to next-to-nothing
        Group()
        {
            for (size_t i = 0; i < N; i++)
            {
                values[i] = T();
            }
        }
*/
};

Documentation for std::copy

With the above you can handle a much wider variety of cases. For example,

int main()
{
    Group<double, 3> v0; //none
    Group<double, 3> v1{1.0}; // too few
    Group<double, 3> v3{1.0, 2.0, 3.0};
    Group<double, 3> v4{1.0, 2.0, 3.0, 4.0}; // too many
    Group<std::string, 3> s2{"A", "B"}; // non-numeric
}

Upvotes: 1

HugoTeixeira
HugoTeixeira

Reputation: 4884

There are a few issues with your code:

Calling another constructor: If you want to call another constructor in C++, you cannot use the this keyword (like in Java). You have to do it after a colon (aka initializer list), like this:

Group(): Group((T[]){0, 0, 0})
{} 

Class definitions end with a semi-colon: In C++, a class definition (templated or not) must end with a semi-colon. You forgot do add it to your code:

class A {
    ...
};  <<--- end with a semi-colon

Raw pointers: Ideally your code shouldn't use raw pointers. There are smart pointers that can make your code more elegant and easier to maintain. You could, for example, use a std::unique_ptr or std::shared_ptr (depending on the case). Here is a simple example in C++14:

auto v1 = std::make_unique<Group<double, 3>>();

Upvotes: 3

Related Questions