C ++构造函数中不需要的隐式转换

我有一个简单的情况,我有一些统一的界面,例如:

class I {
public:
    virtual void Run() = 0;
};

由于性能原因,有些模板具有相同的接口,但未将其声明为虚拟。因此,我需要再增加一层使其功能虚拟化:

template <class B>
class S : public I {
    B mb;

public:
    S(B b)
        :mb(b)
    {}

    virtual void Run()
    {
        std::cout << mb << std::endl; // or mb.Run()
    }
};

请注意,我已替换mb.Run()为打印其值。那只是为了简化,它不会影响我在这里遇到的行为。但是无论如何,到目前为止一切都很好。现在,为了方便起见,我还有一个类可以自动创建接口:

class A {
    I *mi;

public:
    A()
        :mi(0)
    {}

    template <class B>
    A(B b)
        :mi(new S<B>(b))
    {}

    A(A &a)
        :mi(a.mi)
    {
        a.mi = 0;
    }

    template <class B>
    A &operator =(B b)
    {
        delete mi;
        mi = new S<B>(b);
        return *this;
    }

    A &operator =(A &a)
    {
        delete mi;
        mi = a.mi;
        a.mi = 0;
        return *this;
    }

    I *operator ->()
    {
        assert(mi);
        return mi;
    }
};

它既充当自动构造函数,S又同时充当简单的托管指针。我将按以下方式使用它:

A a = instanceOfB();

那应该调用模板A::A<instanceOfB>()该模板S<instanceOfB>在堆上分配一个新的并将其存储在中A现在,我可以呼叫A->Run()最终解析为的S->Run()呼叫instanceOfB::Run(),这将是非虚拟的。

相反,发生的事情是instanceOfB()先转换为A,然后死亡,因为没有构造函数可以使用A(仅A&)。请注意,这仅在g ++中发生,Visual Studio 2008和Visual C ++ 6.0都可以毫无问题地编译代码。您可以使用以下方式重现该行为:

void Test()
{
    A a = 4; // error: no matching function for call to "A::A(A)"
    //A a; a = 4; // works
    //A a(4); // works
    a->Run();
}

我尝试将构造函数声明为显式,但这似乎无济于事,否则我可能做错了。如果A不管理指针,我可以const A&在构造函数中使用value ,那么整个问题都将得到解决。这个问题还有其他解决方案吗?不幸的是C ++ 11不可用。

我正在尝试实现高效的委托。基本上我希望能够做到:

int myFunction(int, float);
StaticCallCtx<int, MakeTypelist(int, float)> ctx = Grab(&myFunction)(1, 2.3f);

// ctx.Run() calls the function, with the specified arguments
// it's *not* virtual (compiles to just a few instructions so I
// don't want to spoil it by adding a vfptr)

AutoCallPointer p = ctx;
// or directly AutoCallPointer p = Grab(&myFunction)(1, 2.3f);
// wraps StaticCallCtx, has ->Run() as well, this time there
// is the price of calling the virtual function

最终,高性能(此后将用于加速某些线性代数函数)和用户舒适度(较短AutoCallPointer p = Grab(fun)(parms)而无需编写模板参数列表)是这里的主要目标。

编辑

@ecatmur的解决方案是正确的。由于时间很短,我将在这里重申。g ++正确地拒绝编译代码,因为A没有复制构造函数可以使用A(仅A&)。在复制初始化的情况下,使用模板构造函数A a = instanceOfB()

我们必须提供一个复制构造函数,采用const A&由于省略了复制,因此无需主体的构造函数声明就足够了。但是,这不是一个很好的解决方案。

最好声明A::miasmutable并将现有的A&构造方法更改为采用const A&(也可以更改copy-operator)。固定A看起来像这样:

class A {
    mutable I *mi;

public:
    A()
        :mi(0)
    {}

    template <class B>
    A(B b)
        :mi(new S<B>(b))
    {}

    A(const A &a)
        :mi(a.mi)
    {
        a.mi = 0;
    }

    template <class B>
    A &operator =(B b)
    {
        delete mi;
        mi = new S<B>(b);
        return *this;
    }

    A &operator =(const A &a)
    {
        delete mi;
        mi = a.mi;
        a.mi = 0;
        return *this;
    }

    I *operator ->()
    {
        assert(mi);
        return mi;
    }
};

该代码可以在g ++和Microsoft的编译器中进行编译(也可以在http://codepad.org/9FqUk0Fj中进行编译)。

恶魔

当复制初始化类类型的对象时,即使删除了副本,复制构造函数也必须可用。g ++拒绝您的程序是正确的;您的旧版本的MSVC不正确,无法接受。

您可能可以提供不带定义的复制构造函数的声明,其依据是在链接时对它的调用将被取消或以其他方式失败。不过,这可能有些令人困惑。

最明显的解决方案是使用直接初始化,正如您已经观察到的那样,它可以正常工作。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章