模板转换为超类

Template cast to super class

本文关键字:超类 转换      更新时间:2023-10-16

我希望能够将类I的对象作为参数的函数传递给FD构造函数::

class D {}; class I: public D {};
FD(std::function<void(D*)> f): _f(f) {}
void test(I*) { std::cout << "Success" << std::endl; }
FD fd(test); fd.call();

正如我一直在研究的那样,我应该实现类型转换,但我不知道一个干净的方法,也没有找到适用于我的案例的解决类型转换的答案。这是完整的代码:

#include <functional>
#include <iostream>
class D {}; class I: public D {};
class FD { 
protected:
std::function<void(D*)> _f;
public:
explicit FD(std::function<void(D*)> f): _f(f) {}
void call() { _f(0); }
};
void test(I*) { std::cout << "Success" << std::endl; }
int main () {
FD fd(test); fd.call();
}

我得到:

test.cpp: In function ‘int main()’:
test.cpp:17:13: error: no matching function for call to ‘FD::FD(void (&)(I*))’
FD fd(test); fd.call();
^
test.cpp:10:14: note: candidate: FD::FD(std::function<void(D*)>)
explicit FD(std::function<void(D*)> f): _f(f) {}
^~
test.cpp:10:14: note:   no known conversion for argument 1 from ‘void(I*)’ to ‘std::function<void(D*)>’

我也尝试过使用intdouble

#include <functional>
#include <iostream>
class FD { 
protected:
std::function<void(double)> _f;
public:
explicit FD(std::function<void(double)> f): _f(f) {}
void call() { _f(0); }
};
void test(int v) { std::cout << "Success" << std::endl; }
int main () {
FD fd(test); fd.call();
}

带输出:

Success

可以用极其痛苦的技术细节来解释编译错误的原因,以及为什么你不能在C++中做你想做的事情,但如果你考虑一个非常简单的思想实验,也许这个根本原因会很清楚:

std::function<void(D*)> f;

您肯定知道,可以使用指向D任何子类的指针来调用这个可调用对象。您可能有另一个名为J的类,它也继承自D,因此这是完全可以接受的:

class J : public D {};
J j;
f(&j);

但您尝试在这里使用指向仅以I *为参数的函数的指针来构造std::function

void test(I*)

如果您试图执行的操作是可能的,那么这意味着可以通过std::function<void (D*)> object使用指向J而不是I的指针来调用此test()函数。失败当然,这在C++中是不允许的。如果两个类之间的唯一关系是它们都有相同的父类,那么您就无法将指向其中一个类的指针转换为指向另一个的指针。C++不能以这种方式工作。

第二个例子intdouble工作的原因是intdouble可以相互转换。对于指向两个随机类的指针,情况并非如此。只有在某些定义明确的情况下,指向类的指针才能转换为指向不同类的指针。C++中不允许您想要什么。

这里唯一可能发生的是test((函数,它将D *作为参数,交叉手指并尝试将其dynamic_castI *(假设D满足动态可浇注类的要求(。然后,您将不得不决定当转换失败时会发生什么。

当然,您可以这样执行强制转换:FD fd(reinterpret_cast<void(*)(D*)>(test));,但这是UB。

错误很明显——不能将D*隐式转换为I*。这违反了多态性。

您确定没有混淆ID吗?

UPD:

我认为否决这个帖子意味着它不清楚。我将尝试解释:

想象一下:

class D
{
public:
void (*ok)() = +[] { std::cout << "OK"; };
};
class I: public D
{
public:
void (*not_ok)() = ok;
};

CCD_ 23可以从CCD_ 24实例访问,而CCD_。现在您想调用函数,将指针指向I。但这意味着您的函数可以调用not_ok(),这对于指向D:的指针无效

void test(I* i)
{
i->ok();
i->not_ok();
}

使用原始指针,您可以尝试以下操作:

class FD { 
protected:
std::function<void(D*)> _f;
public:
explicit FD(void(*f)(I*)) : _f(reinterpret_cast<void(*)(D*)>(f))
{
}
void call() { _f(new I); }
};

它将打印OK。但如果你用new D替换new I,它将在not_ok()调用时失败

这就是为什么C++不允许隐式地强制转换指针(而您仍然可以隐式地将int强制转换为double(。

即使你想"破解"你的代码,C++也没有全局类型转换规则。此外,您无法从std::函数中获取原始指针,因此,没有办法做到这一点。