1

In the code below the initializer list is initialized with a B and a C object whose ctors pass in values “bbb” and 333 respectively. Since these objects derive from A, the list properly creates two A elements.

#include <string>
#include <vector>
#include <iostream>

struct A 
{
  char ch;
};

struct B : A 
{ 
  B( std::string str ) : str( str ) {}
  std::string str;
};

struct C : A 
{ 
  C( int num ) : num( num ) {}
  int num;
};

struct D
{
  D( std::initializer_list< A* > initializerList )
    : variadicVect( initializerList )
  {}

  std::vector< A* > variadicVect;
};

int main()
{
  D d { new B { "bbb" }, new C { 333 } };
  d.variadicVect[ 0 ]->ch = 'm'; // ok, but accesses base member
  //std::cout << d.variadicVect[ 0 ]->str << std::endl; // error C2039: 'str': is not a member of 'A'
  //std::cout << d.variadicVect[ 1 ]->num << std::endl; // error C2039: 'num': is not a member of 'A'

  return 0;
}

A similar problem was reported in link resolved by @Jarod42. He identified Object Slicing as the problem. To avoid this assignment-by-value issue, I new’d the initializer list object, but am still unable to access the derived class.

Another attempt I tried was to templatize the initializer_list type (code not shown), but I got this error: ‘D::variadicVect': only static data member templates are allowed’

My goal is to be able to initialize the list with various derived objects choosing the derived type at compile time and yet be able to access them properly at runtime. Is that possible?

Community
  • 1
  • 1
rtischer8277
  • 496
  • 6
  • 27
  • 1
    Think about what you are asking the code to do. You have a `std::vector` of `A` pointers. Does `A` know about the members you are trying to access? – NathanOliver Jun 02 '16 at 18:44
  • There is no slicing in this code. The linked question is substantially different. The problem in this code is that `A` has no member `str`, so you cannot apply `->str` to an `A *`. – M.M Jun 02 '16 at 22:46

2 Answers2

3

To make your code compile you would have to cast your vector elements to appropriate types:

std::cout << static_cast<B*>(d.variadicVect[ 0 ])->str << std::endl;
std::cout << static_cast<C*>(d.variadicVect[ 1 ])->num << std::endl;

otherwise C++ sees them as type A and only interface from A is available.

Other solution is to add virtual functions (virtual std::string Str(){return "";} and virtual int Num(){return 0;} ) to A and override them in base classes, where needed. Then you will not have to cast. example below:

http://coliru.stacked-crooked.com/a/ed7c73a5d8afa5e9

#include <string>
#include <vector>
#include <iostream>

struct A 
{
  char ch;
  virtual std::string Str() { return ""; }
  virtual int Num() { return 0; }
};

struct B : A 
{ 
  B( std::string str ) : str( str ) {}
  std::string str;
  std::string Str() override { return str; }
};

struct C : A 
{ 
  C( int num ) : num( num ) {}
  int num;
  int Num() override { return num; }
};

struct D
{
  D( std::initializer_list< A* > initializerList )
    : variadicVect( initializerList )
  {}

  std::vector< A* > variadicVect;

};

int main()
{
  D d { new B { "bbb" }, new C { 333 } };
  d.variadicVect[ 0 ]->ch = 'm'; // ok, but accesses base member
  std::cout << d.variadicVect[ 0 ]->Str() << std::endl;
  std::cout << d.variadicVect[ 1 ]->Num() << std::endl;

  return 0;
}
marcinj
  • 48,511
  • 9
  • 79
  • 100
  • I think the static_cast makes better sense for me since I know by definition what type each vector element will be at compile time. – rtischer8277 Jun 02 '16 at 19:15
  • Actually static_cast is not that good, if you by any chance add instance of wrong type you will get UB at runtime. Best aproach is to make D a template, this will catch any such mistakes at compile time. – marcinj Jun 02 '16 at 19:32
  • Please elaborate on making D a template. I don't see how this would affect the vector A* objects. – rtischer8277 Jun 02 '16 at 20:46
  • What about using nested classes? That is, A would contain both B and C and scoping at runtime could be used to access the elements like A::B. I am surprised there is not a neater solution to the problem since all the information is there at compile time. – rtischer8277 Jun 02 '16 at 20:49
  • See example here: http://coliru.stacked-crooked.com/a/ad80a685d685f5b8 for templated solution, I dont see how nested classes could help here. `the information is there at compile time` actually its not, in your example you can initialize `d` with whatever `A` derived class instance at runtime so you have to implement the logic to find out which class type is at specified index in `d`. This is actually what virtual functions and polymorphism is doing for you. – marcinj Jun 02 '16 at 22:35
0

This question was neatly implicitly answered in another one of my postings (see its Edit section for my explanation of why it took so long for me to figure out what the right question was, after which the solution - union-like classes - was just a matter of getting the constructors and destructors right): using a union-like class in an std::initializer_list.

Community
  • 1
  • 1
rtischer8277
  • 496
  • 6
  • 27