initializer cannot be of abstract type

Hello friends, i have the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct Foo
{
  virtual void Pure() = 0;
};

template <typename T>
struct Bar : Foo
{
  virtual void Pure() override
  {

  }

};

struct Baz
{
  Baz(std::initializer_list<Foo> foos){}

};

int main()
{

  Baz
  {
    Bar<int>{},
    Bar<float>{}
  };

  return 0;
}

Baz needs to be constructed with a list of child classes of Foo, which is an abstract class. The problem is, apparently, initializer lists (& other list containers i've checked such as vector) cannot hold abstract classes.

I've tried changing the type from Foo to Foo&, however as it turns out containers cant hold references either.
I tried using std::reference_wrapper instead, which the initializer_list doesn't complain about, however then compiler complains that there are "no matching function call to Baz::Baz(<brace-enclosed initializer list>)"

i'm guessing its because the std::reference_wrapper cannot be constructed from the rvalues that im passing? So it cannot convert the initializer list of Foos to initializer list of reference_wrapper<Foo>.

So my question is what do?


The only solution i can think of is to make the initializer_list of type Foo* and then doing new Bar<int>{} instead (or using smart pointers), however i dont like this as it seems to be needless, i should be able to do it without heap allocation, shouldn't i?

thanks & thanks
Last edited on
the container is not the problem. You can't make instances of abstract classes because they lack implementation for their methods -- you have to inherit them into another class that provides this..

you can also make it not abstract, by providing the method bodies (they can still be virtual and over-ridden when inherited).

perhaps baz should really have a list of bars? That would be ok.
Last edited on
you can also make it not abstract, by providing the method bodies (they can still be virtual and over-ridden when inherited).

i actually tried that but for some reason when i then iterate over the list & call the Pure method, the base classes method is invoked?

(here my code for that)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
struct Foo
{
  virtual void Pure() const
  {
    std::cout << "foo\n";
  }
};

template <typename T>
struct Bar : Foo
{
  virtual void Pure() const override
  {
    std::cout << "bar\n";
  }
};

struct Baz
{
  Baz(std::initializer_list<Foo> bars)
  {
    for(const Foo& foo : bars)
      foo.Pure();
  }

};

int main()
{

  Baz
  {
    Bar<int>{},
    Bar<float>{}
  };

  return 0;
}


perhaps baz should really have a list of bars? That would be ok.

yes that would be fine but i dont know how i can accept a generic list of bars when they are templated?
Last edited on
i figured maybe i should post what im actually trying to achieve with this:
Basically im trying to write some code that makes it easier for me to print variables (the variable name itself & its value) in a nicely formatted way.

This is what i had envisioned.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Foo
{
  int a,bar,carrot;
};

Foo foo{1,2,3};
int other{500};

PRINT_GROUP(Info)
{
  PRINT_VAR(foo.a),
  PRINT_VAR(foo.bar),
  PRINT_VAR(foo.carrot),
  PRINT_VAR(other)
}

//output (notice how its all nicely aligned & indented):
Info:
  foo.a:       1
  foo.bar:     2
  foo.carrot:  3
  other:       500


This is basically my implementation of that:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#define PRINT_GROUP(groupName, ...) PrintVarGroup{#groupName, ...}
#define PRINT_VAR(var) PrintVarT{#var, var}

class PrintVar
{
  public:
    PrintVar(const char* varName): m_varName{varName} {}
    virtual ~PrintVar() = default;

    virtual void PrintName()  const
    {
      std::cout << m_varName;
    }

    virtual void PrintValue() const = 0

    virtual const char* GetVarName() const
    {
      return m_varName;
    }

  private:
    const char* m_varName;

};

template <typename T>
class PrintVarT : public PrintVar
{
  public:
    PrintVarT(const char* varName, const T& var)
    :PrintVar{varName}, m_var{var} {}
    virtual ~PrintVarT() = default;

    virtual void PrintValue() const override
    {
      std::cout << m_var;
    }

  private:
    const T& m_var;
};

class PrintVarGroup
{
  public:
    PrintVarGroup(const char* groupName, std::initializer_list<PrintVar> printVars)
    {
      std::cout << groupName << std::endl;

      for(PrintVar printVar : printVars)
      {
        std::cout << "  ";
        printVar.PrintName();
        std::cout << ":           "; //temporary (in the future calculate based on difference between varName & longest varName in group to ensure same alignment for each value field)
        printVar.PrintValue();
        std::cout << std::endl;
      }
    }

    virtual ~PrintVarGroup() = default;
};


but yeah it has the problem that PrintVar is abstract and PrintVarT is templated
Last edited on
you are working way too hard!
to print a variable's type, try
typeid(a).name()
and if the object has a << operator friend, you can, for all your types, just say
cout << typeid(anything).name() << " " << anything;

there is probably a way to rescue what you have and force it to work for fun...
Last edited on
I1 is an abstract class because it has purely virtual functions (= functions without a definition).

You cannot create instances of abstract classes (because how would they work?!), therefore a declaration such as I1 a doesn’t work.

After your edit to the question it seems as though I1 shouldn’t be an abstract class, since you provided definitions for the methods. If that’s the case, just remove the = 0 after your method declarations to make the code work.
If you need this, consider incorporating a capable debugger into your workflow. Any debugger can do this and far more, with more control and less effort.

I dont know how i can accept a generic list of bars when they are templated?

Make the instances of the class template inherit from the same abstract base class. Then, work with the base class. Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
struct printer_impl_base 
{
  virtual ~printer_impl_base() = default; 
  virtual void do_print() const = 0; 
};

template <typename T> 
  class printer_impl: public printer_impl_base 
  { 
    T x;
  public:
    explicit printer_impl(T x): x(static_cast<T&&>(x)) {}  
    void do_print() const override { std::cout << x; } 
  };
    
class printer
{    
  printer_impl_base* base;

public:
  ~printer() { delete base; }
  printer(printer const&) = delete;
  printer(printer&&) noexcept = default;
  printer& operator=(printer const&) = delete;
  printer& operator=(printer&&) noexcept = default; 
  
  template <typename T>
    /* explicit(false) */ printer(T x): base(new printer_impl<T>{x}) {}
    
  void print() const { base->do_print(); }
}; 

int main()
{
  printer ps[] = { "hello", ' ', 123, "... ", 42.5, "!\n" };
  
  for (printer const& p: ps) p.print();
}


If we talk fundamentals, this still does a lot of unnecessary work, because obviously you do not need to copy elements into heap-allocated storage just to print them all. Nor do you need dynamic dispatch to print the values of objects that are already listed in the source code. The compiler already has all the important information; it's just a matter of instructing it to generate the right code.

There is no way to rescue this approach if "no allocation, ever" or "no extra overhead" is an important criterion.
Last edited on
Topic archived. No new replies allowed.