我有一个简单的情况,我有一些统一的界面,例如:
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&
,这样就可以解决整个问题。还有另一个解决这个问题的方法吗?很遗憾,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
声明为mutable
并将现有的A&
构造函数更改为const A&
(复制运算符也可能会更改)。固定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中)。
答案 0 :(得分:2)
复制初始化类类型的对象时,复制构造函数需要可用,即使复制被省略也是如此。 g ++拒绝你的程序是正确的;您的旧版本的MSVC不正确接受它。
您可以在没有定义的情况下提供复制构造函数的声明,因为在链接时将忽略对它的调用或以其他方式失败。但这可能有些令人困惑。
最明显的解决方案是使用直接初始化,正如您已经观察到的那样正常。