1

I have an abstract struct I with method a. B and B2 will inherit from it. X struct has an I type member and will instantiate it via createInsance template method based on type. I want to have on B2 an additional function b2Exclusive but I got compilation error that it is not present in A.

error: ‘using element_type = struct B’ {aka ‘struct B’} has no member named ‘b2Exclusive’

Is any way for solving this without defining b2Exclusive for B as well and to keep to structure this way?

#include <iostream>
#include <memory>
using namespace std;

struct I
{
    virtual void a() = 0;
};

struct B : public I 
{   
    B()
    {
        std::cout<<"B\n";
    }
    void a()
    {
        std::cout<<"-a from B\n";
    }
};

struct B2 : public I
{ 
  B2()
  {
    std::cout<<"B2\n";
  }
  void a()
  {
    std::cout<<"-a from B2\n";
  }
  void b2Exclusive()
  {
       std::cout<<"-something for B2\n";
  }
};

using Iptr = std::shared_ptr<I>; 

struct X
{
    void createI()
    {
        if (type == "B")
        {
            createInstance<B>();
        }
        else 
        {
            createInstance<B2>();
        }
        
    }
    
    template <typename T>
    void createInstance()
    {
        auto i = std::make_shared<T>();
        if (type == "B2")
        {
            i->b2Exclusive();
        }
        
    }
    std::string type = "None";
};

int main()
{
    
    X x;
    x.type = "B2";
    x.createI();

    return 0;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
John Dean
  • 13
  • 2
  • If some `I`s can do `b2Exclusive` then it makes sense to add it to the shared interface. You can implement it there as a no-op function so that you don't need to implement it in `B` or any other implementations that might come along later. Alternatively, you can [downcast](https://stackoverflow.com/questions/17417848/stdunique-ptr-with-derived-class), but that's usually a design smell. – Thomas May 24 '22 at 07:13
  • Templates are a compile time mechanism. You're trying to control instantiation of classes (the actual type of objects) using the value of an `std::string`, which means the value is not determined until run time. That's true even if the value of the string is written in source code. – Peter May 24 '22 at 07:17
  • Why isn't `b2Exclusive` called from B2's constructor? – n. m. could be an AI May 24 '22 at 07:17
  • thank you all! @n.1.8e9-where's-my-sharem. I need some values for b2Exclusive from X – John Dean May 24 '22 at 07:19
  • So pass X to the constructor. – n. m. could be an AI May 24 '22 at 07:23
  • @n.1.8e9-where's-my-sharem. I only need it for B2 not for all which are using I interface – John Dean May 24 '22 at 07:30
  • You can pass it only to B2. Add an additional template parameter pack to `createInstance` and forward it to the constructor. `createInstance()` and `createInstance(this)`. Alternatively, pass it always and just ignore where not needed (uniform interface. suitable for a factory with a large number of produced types). – n. m. could be an AI May 24 '22 at 07:40
  • @n.1.8e9-where's-my-sharem. what is your opinion about the current answer ? – John Dean May 24 '22 at 08:54
  • I don't quite like it for two reason. (1) I don't like initialisation that is not done entirely by a constructor, therefore I think that the most natural place to call `b2Exclusive` is the constructor of `B2`. (2) If your program grows to have a factory that creates many classes, each with its own quirks, you will need to write a new specialisation each time there is a new class with its own exclusive thing. – n. m. could be an AI May 24 '22 at 09:42
  • @n.1.8e9-where's-my-sharem. can you give other answer, I do not need entire X in B2 only an attribute from it, type in this case, thank you! – John Dean May 24 '22 at 10:28

1 Answers1

0

You can only call b2Exclusive if the template function use typename B2: one way to do so is to create the specialization for that type, such as this for example:

struct X
{
    void createI();
    
    template <typename T>
    void createInstance()
    {
        //do something
        
    }
    
    std::string type = "None";
};

template<>
void X::createInstance<B2> ()
{
    auto i = std::make_shared<B2>();
    i->b2Exclusive();
}

void X::createI()
{
    if (type == "B")
    {
        createInstance<B>();
    }
    else 
    {
        createInstance<B2>();
    }
    
}

int main()
{
    
    X x;
    x.type = "B2";
    x.createI();

    return 0;
}
ErniBrown
  • 1,283
  • 12
  • 25