0

I have a base class.

#include <string.h>
class Channel
{
private:
    std::string stdstrName;

public:
    Channel() : stdstrName("CHANNEL"){   }
    Channel(std::string name) : stdstrName(name){ }
    void PrintName() { std::cout << stdstrName << std::endl; }  
};

which is inherited by Position class.

class PositionChannel : public Channel
{
public:
    std::vector<int> keyframes;
    PositionChannel() : Channel("POSITION") , keyframes( { 1 , 2, 3 }) {    }
};

There is a director class which has the channel clas as its data members.

#include "Channel.h"
#include <memory>

class Director
{
private:
    std::vector<std::shared_ptr<Channel>> channels;

public:
    void AddChannel(std::shared_ptr<Channel> chn) { channels.push_back(chn); }
    void GetChannel(Channel **chn) { *chn = channels[0].get(); }   
};

now when in the main function.

 // Free function
 template<typename T>
    void GetChannel(Director *dir)
    {
        T *chn;
        dir->GetChannel(&chn);
    }

    Director dir;
    PositionChannel channel;
    std::shared_ptr<Channel> channelPointer = std::make_shared<Channel>(channel);
    dir.AddChannel(channelPointer);
    GetChannel< PositionChannel>(&dir); // here i get error  

this is the error message error C2664: ' cannot convert argument 1 from 'T **' to 'Channel **

if i change the templated function to a non templted function than i do not get any error.

Summit
  • 2,112
  • 2
  • 12
  • 36
  • 2
    "Un-templating" `GetChannel` using `PositionChannel* chn;` doesn't work either. A minimal example of the problem is `int main() { PositionChannel* pc; Channel** c = &pc; }`. – molbdnilo Jul 17 '20 at 12:12
  • It's the same as if you did: `PositionChannel* chn; dir.GetChannel(&chn);`. Does that work? Nope. – user253751 Jul 17 '20 at 14:04

3 Answers3

4

In you GetChannel call, &chn argument is of type PositionChannel**, but the type of Director::GetChannel parameter is Channel**. These two types are not convertible; see, for example this question: Conversion of pointer-to-pointer between derived and base classes?.

I am not sure what are your intentions since the code does not make much sense as is, but you can redefine GetChannel as follows:

template<typename T>
void GetChannel(Director *dir)
{
  Channel* ptr;
  dir->GetChannel(&ptr);
  T *chn = ptr;
}
Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
1

T can be any type, you can't convert it to a Channel for any type.

There are probably ways to make it work with templates, but I feel like your problem could be more easily solved by using polymorphism with something like this :

void GetChannel(Channel* chn, Director *dir)
{
    dir->GetChannel(&chn);
}

And then chn can be any type dervived from Channel.

ShadowMitia
  • 2,411
  • 1
  • 19
  • 24
  • 1
    The OP doesn't want to convert any type to `Channel`, but a subclass of `Channel`. – lubgr Jul 17 '20 at 11:59
  • I guess, but it's the same problem. T will be too generic to be used as is, no? – ShadowMitia Jul 17 '20 at 12:01
  • @ShadowMitia yes i am able to do it without a non templated function but wanted to check if this can be done through templates. – Summit Jul 17 '20 at 12:03
  • @Summit Right, misunderstood your question a bit. I'm sure there are ways to do it, but I won't know them^^ – ShadowMitia Jul 17 '20 at 12:09
  • @Summit The non-template version doesn't work either: https://godbolt.org/z/v69hjE – perivesta Jul 17 '20 at 12:10
  • Is the type in the non-template function `PositionChannel*`? Because that is the problem. `Channel*` works – perivesta Jul 17 '20 at 12:13
  • Sounds like a compiler extension that accepts conversion from `Derived**` to `Base**`. GCC would accept it with `-fpermissive` flag. What compiler do you use @Summit ? – Yksisarvinen Jul 17 '20 at 12:14
0

Yes, Daniel Langr already gave the correct answer. I added a check in the template if the class is derived.

#include <type_traits>
class Channel
{ 
    public:
    virtual ~Channel() = default;
};

class PositionChannel : public Channel
{ 
};

struct Director{
    void GetChannel(Channel **c) {}
};
 
template <typename T,
typename = typename std::enable_if<std::is_base_of<Channel, T>::value, T>::type>
void GetChannel(Director *dir)
{
    Channel *chn;
    dir->GetChannel(&chn);
    T* ptr = static_cast<T*>(chn);
}

int main(void) {
    Director dir;
    GetChannel<PositionChannel>(&dir);
    return 0;
}
Vasilij
  • 1,861
  • 1
  • 5
  • 9