【问题标题】:Template cast to super class模板转换为超类
【发布时间】:2020-06-07 15:10:00
【问题描述】:

我希望能够以 I 类的对象作为参数传递函数,其中 I 继承自 D,到 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();

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

#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

【问题讨论】:

  • 我对@9​​87654327@ 和double 有效的情况感到惊讶,但这可能是因为intdouble 可以相互隐式转换。 D* 不能隐式转换为 I*,只有相反的情况。如果你有class I {}; class D: public I {};,它会起作用。想象一下如果I 有一个额外的成员。目前尚不清楚您要实现什么行为。
  • 您好@FrançoisAndrieux,exaclty,如果FD 调用不在D 中的I 方法怎么办?如果期望一个 I 并给出一个 D,它肯定会崩溃。也许在预期 D 时给 I 是可以原谅的,以禁止不太确定性的崩溃?

标签: c++ functional-programming


【解决方案1】:

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

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

您肯定明白,可以使用指向Dany 子类的指针来调用这个可调用对象。您可能还有其他一些名为 J 的类也继承自 D,因此这是完全可以接受的:

class J : public D {};

J j;

f(&j);

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

void test(I*)

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

而你的第二个例子,ints 和 doubles 工作的原因是因为 ints 和 doubles 可以互相转换。指向两个随机类的指针并非如此。只有在某些明确定义的情况下,才能将指向类的指针转换为指向不同类的指针。 C++ 不允许你想要的东西。

这里唯一可能发生的事情是test() 函数将D * 作为参数,交叉手指并尝试将dynamic_cast 传递给I *(假设D 满足动态可转换类的要求)。然后你必须决定当这个转换失败时会发生什么。

【讨论】:

    【解决方案2】:

    当然,您可以像这样进行演员表:FD fd(reinterpret_cast&lt;void(*)(D*)&gt;(test)); 但这是 UB。

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

    您确定没有混淆ID

    UPD:

    我认为对这篇文章投反对票意味着它还不清楚。我会尽力解释:

    想象一下:

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

    ok() 可以从 D 实例访问,而 not_ok() 不能。现在你想调用你的函数,将指针指向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); }
    };
    

    它将打印 OKOK。但是,如果您将new I 替换为new D,它将在not_ok() 调用时失败

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

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

    【讨论】:

    • 你好 espkk,reinterpret_cast 和替换 I 作为 D 之父的工作。另一方面,带有原始指针的代码与 new I 一起使用,而不是与 new D 一起使用。我给了你+1,因此其他人应该是-1。我猜想原因是我的代码违反了与多态不同的东西,否则 *new D" 应该工作而不是 *new I" 如你所说。
    • @Matias Haeussler,感谢您的关注。确实,我把事情搞混了,我的意思是new I。现已更正。所以,new D 在这种情况下不起作用(会导致崩溃),因为它是对not_ok 成员一无所知的基类。 new I 可以正常工作,因为它源自 D 并且具有两个成员
    猜你喜欢
    • 2016-10-21
    • 2017-01-13
    • 1970-01-01
    • 2018-05-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-26
    • 2020-07-08
    • 2015-06-27
    相关资源
    最近更新 更多