我有一个简单的情况,我有一些统一的界面,例如:
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::mi
asmutable
并将现有的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] 删除。
我来说两句