1

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?

NetherGranite
  • 1,940
  • 1
  • 14
  • 42
  • 1
    `this((T[]){0, 0, 0})` makes no sense and [does not compile](https://ideone.com/W6BfjN). – n. m. could be an AI Aug 26 '18 at 02:50
  • You're cherry picking your error messages. There are at least two errors that will be diagnosed and one suspicious code construct that may trigger warnings (all in the header file) before reaching `main()`, and they will contribute to the compiler being confused about existence of a constructor with no arguments (and therefore diagnosing the error you report). – Peter Aug 26 '18 at 02:51
  • 2
    Side note: placing an array of three elements into a constructor expecting an `N`-element array is a bad idea. And what if `T` is a `std::string`? Zeroes don't make much sense. – user4581301 Aug 26 '18 at 02:56
  • @user4581301 Shoot, you're right. I don't know what I was thinking. – NetherGranite Aug 26 '18 at 04:03
  • Please don't edit the question like that after it has been answered. You can post a new question if you like – M.M Aug 26 '18 at 04:58

3 Answers3

3

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>>();
HugoTeixeira
  • 4,674
  • 3
  • 22
  • 32
  • 1
    Thank you so much for your thoughtful and organized answer. I made a few typos in my question, and the error message I was getting turned out to be a problem with the IDE entirely. The new problem I am dealing with, however, is solved by your first point about constructor delegation, so I will mark it as accepted. – NetherGranite Aug 26 '18 at 04:23
  • `Group(): Group((T[]){0, 0, 0})` is a syntax error in Standard C++. There cannot be a braced list after a parenthesized type name – M.M Aug 26 '18 at 04:56
  • @M.M Good catch. I did not think to check that compound literals are one case where the C++ standard is not backwards compatible with C. It appears, however, that GCC and Clang support them as an extension. – NetherGranite Aug 26 '18 at 05:44
1

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>();
}
Matthew Fisher
  • 2,258
  • 2
  • 14
  • 23
  • Thank you for your answer. The error message I was getting turned out to be an issue with the IDE. The new error I had to face, however, was trying to call a constructor from within another constructor. How were you able to get this to compile? – NetherGranite Aug 26 '18 at 04:26
  • @NetherGranite Compiles in recent versions of the Clang compiler. I'm not sure why, but I don't claim to be a language lawyer. Could be a language extension or maybe I'm reading what's legal and illegal wrong. – user4581301 Aug 26 '18 at 04:29
  • @user4581301 Huh, how strange. Such functionality is not even part of an already-existing standard or a soon-to-be-implemented standard? – NetherGranite Aug 26 '18 at 04:33
  • @NetherGranite My apologies. I hadn't noticed Mathew had updated the answer to eliminate my primary complaint with a previous version. The new version does not appear to compile with any compilers I have at my disposal. – user4581301 Aug 26 '18 at 04:43
  • Latest version should work with gcc and clang. It appears the same as the one from the OP however. @NetherGranite try to put it all in one file to rule out other unrelated issues. – Matthew Fisher Aug 26 '18 at 12:22
1

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
}
user4581301
  • 33,082
  • 7
  • 33
  • 54