无法将参数1从“Component <w> *”转换为“Component <w> *”

时间:2016-06-05 16:36:40

标签: c++ templates

我创建了一个小样本来演示这个问题。我当然不明白这里出了什么问题。使用Visual Studio 2010。

这些类(非常宽松地)在MFC之后建模,因为这是我需要使用它的。 基本上我想创建一个面板类,它可以包含其他面板或控件,所以我添加了一个跟踪id和父元素的组件类,以及作为它组件的容器的面板类。

现在我不明白为什么我得到这个编译器错误以及为什么我只在我使用控件而不是子面板时才能得到它。

#include <iostream>
#include <algorithm>
#include <vector>

class CWnd
{
public:
    CWnd(CWnd *pParent = NULL, int nId = -1)
    {
        mId = nId;
        mParent = pParent;
        mClassname = NULL;
    }

    void setId(int nId) { mId = nId; }
    int getId(void) const { return mId; }

    void setParent(CWnd *pParent) { mParent = pParent; }
    CWnd *getParent(void) const { return mParent; }

    bool create(const char *pClassname, int nId, CWnd *pParent)
    {
        mId = nId;
        mParent = pParent;
        mClassname = pClassname;

        return true;
    }

private:
    int mId;
    CWnd *mParent;
    const char *mClassname;
};

class Ctrl : public CWnd
{
public:
    Ctrl(CWnd *pParent, int nId = -1)
        : CWnd(pParent, nId)
    {
    }
};

class Dialog : public CWnd
{
public:
    Dialog(CWnd *pParent, int nId = -1)
        : CWnd(pParent, nId)
    {
    }

    bool create(int nId, CWnd *pParent)
    {
        CWnd::create("dialog", nId, pParent);

        return true;
    }
};

class View : public CWnd
{
public:
    View(CWnd *pParent = NULL)
        : CWnd(pParent)
    {
    }
};

template <typename W>
class Component : public W
{
public:
    Component(CWnd *pParent = NULL)
        : W(pParent)
    {
        mId = -1;
        mParent = pParent;
    }

    virtual bool create(CWnd *pParent = NULL)
    {
        if(pParent)
            mParent = pParent;

        W::setParent(mParent);
        W::setId(mId);

        return true;
    }

private:
    int mId;
    CWnd *mParent;
};

class Panel : public Component<Dialog>
{
public:
    Panel(CWnd *pParent = NULL)
        : Component(pParent)
    {
    }

    virtual bool create(CWnd *pParent = NULL)
    {
        if(pParent != NULL)
            setParent(pParent);

        Dialog::create(getId(), pParent);

        for(Components::iterator it = mComponents.begin(); it != mComponents.end(); ++it)
        {
            if(!(*it)->create(this))
                return false;
        }

        return true;
    }

    void addComponent(Component *pComponent)
    {
        if(std::find(mComponents.begin(), mComponents.end(), pComponent) == mComponents.end())
            mComponents.push_back(pComponent);
    }

    void removeComponent(Component *pComponent)
    {
        Components::iterator pos = std::find(mComponents.begin(), mComponents.end(), pComponent);
        if(pos != mComponents.end())
            mComponents.erase(pos);
    }

protected:
    typedef std::vector<Component *> Components;

private:
    Components mComponents;
};

int main()
{
    View v;
    Panel d;
    Panel p;
    Component<Ctrl> listbox;
    Component<Ctrl> tab;

    // these are generating the error
    d.addComponent(&listbox);
    d.addComponent(&tab);

    // only this one works.
    d.addComponent(&p);

    std::cout << "\nDone! Press any key..." << std::endl;
    std::cin.ignore();

    return 0;
}

我得到的错误信息是:

'Panel::addComponent' : cannot convert parameter 1 from 'Component<W> *' to 'Component<W> *'

3 个答案:

答案 0 :(得分:3)

没有类型Component,因此没有类型Component*。但是,在Panel类的上下文中,由于它来自Component<Dialog>,因此名称Component实际上已注入,并引用Component<Dialog>。所以addComponent的正确签名是:

void addComponent(Component<Dialog> *pComponent)

Component<Ctrl>并非来自Component<Dialog>,因此无法将Component<Ctrl>*传递给此函数。

您可能想要做的是从第二个基类派生Component(不是从任何东西派生的)。

class Component_base
{
    // component common functionality
};

template<typename W>
class Component : public W, public Component_base
{
    ...
};

然后你的Panel班可以存储:

std::vector<Component_base*> Components;

答案 1 :(得分:1)

有一种叫做injected-class-name的东西。对于类模板,这意味着在类模板的范围内,类模板名称是指特定的实例化 - 而不是模板本身:

template <class T>
class C {
    C(C<T> const& ) = default; // one way to write the copy constructor
    C(C const& ) = default;    // exactly equivalent to the above
};

继承的类也继承了所有注入的类名。所以当你写:

class Panel : public Component<Dialog>

我们有多个不同的注入名称:PanelComponentDialogCWnd。它是第二个重要的:在Panel的范围内,Component指的是特定的类Component<Dialog>不是课程模板Component。因此:

void addComponent(Component *pComponent)

是(并且完全相同)的缩写:

void addComponent(Component<Dialog> *pComponent)

Component<Dialog>Component<Ctrl>之间没有阶级关系,因此错误。

根据您的评论,您实际上希望这是一个功能模板。所以你必须明确地把它做成一个:

template <class W>
void addComponent(Component<W> *pComponent) { ... }

但是,由于你持有vector<Component<Dialog>*>,这项工作不会奏效。你也需要改变它 - 也许是vector<CWnd*>

答案 2 :(得分:0)

您的问题是没有从Component<Ctrl>*转换为Component<Dialog>*d.addComponent(&p);有效,因为dp属于同一类型。您需要为Component<Ctrl>*Component<Dialog>*创建公共基类,以便在这两种类型之间进行此类转换或创建转换构造函数。

相关问题