模板参数推导与 QT lambda 不匹配

Posted

技术标签:

【中文标题】模板参数推导与 QT lambda 不匹配【英文标题】:Template Argument deduction fails to match with QT lambda 【发布时间】:2017-03-04 20:04:58 【问题描述】:

我有一个使用 QT 5.7 和 Visual Studio 2015 的项目,它发出关于推导规则的编译器错误。就演绎规则而言,我有点新手,所以我想看看是否有人可以告诉我如何解决这个问题,同时解释函数签名匹配的情况。

我正在尝试使用基于模板的智能连接器来管理我的 QT 项目中的信号和插槽。我从 Stack Overflow 中的 this Q&A 获得了这种信号/插槽管理方法的灵感。应该自动管理信号/槽的模板代码如下:

//! see https://***.com/questions/26553029/
//! how-to-disconnect-a-lambda-function-without-storing-connection.
using ListenToken = std::shared_ptr<void>;

struct Disconnecter 
    QMetaObject::Connection mConnection;
    explicit Disconnecter(QMetaObject::Connection&& conn)
        : mConnection(std::move(conn))
    

    ~Disconnecter() 
        QObject::disconnect(mConnection);
    
;

template<class F, class T, class M>
ListenToken QtConnect(T* source, M* method, F&& f) 
    return std::make_shared<Disconnecter>(
        QObject::connect(source, method, std::forward<F>(f))
    );


using SignalSlotInfo = std::vector<ListenToken>;

在我的主窗口类中,我有一个 SignalSlotInfo 成员,用于跟踪信号/插槽,以便在应用程序关闭某些 UI 事件时这些可以自动断开连接。

我要连接的有问题的 QT 对象是 QSerialPort。我正在尝试将 QSerialPort 的“写入字节”信号(从其 QIODevice 父级继承)连接到 lambda 中的插槽,但它无法编译并出现以下错误:

1>mainwindow.cpp(1246): error C2672: 'QtConnect': no matching overloaded function found
1>mainwindow.cpp(1246): error C2784: 'ListenToken QtConnect(T *,M *,F &&)': could not deduce template argument for 'M *' from 'void (__cdecl QIODevice::* )(qint64)'
1>  c:\users\johnc\main\app739\app739\mainwindow.h(58): note: see declaration of 'QtConnect'

如果我尝试在不使用 lambdas 的情况下执行此操作(使用 QT 连接调用),它可以正常工作......

// Connect Tx/Rx handlers
connect(mPort.get(), &QSerialPort::bytesWritten, this, &MainWindow::processTx);

但是在 mainwindow.cpp 中调用 QtConnect 模板如下:

// Connect Tx/Rx handlers
QtConnect(mPort.get(), &QSerialPort::bytesWritten, [&](qint64 bytes) 
    ...
);

导致上述错误。 mPort 是一个指针 std::unique_ptr&lt;QSerialPort&gt;

bytesWritten 信号继承自 QSerialPort 的父类QIODevice,并具有签名void bytesWritten(qint64 bytes)

【问题讨论】:

【参考方案1】:

M* 替换为M

template<class F, class T, class M>
ListenToken QtConnect(T* source, M method, F&& f) 
    return std::make_shared<Disconnecter>(
        QObject::connect(source, method, std::forward<F>(f))
    );

这背后的原因是,如果您离开M*,编译器将尝试通过将M* 设置为void (QIODevice::*)(qint64) 来解析模板。但是void (QIODevice::*)(qint64)pointer-to-member,而不是pointer,而M* 明确是pointer。这种矛盾导致编译器无法解析模板。

【讨论】:

以上是关于模板参数推导与 QT lambda 不匹配的主要内容,如果未能解决你的问题,请参考以下文章

默认与推导模板参数?

深入了解C++:auto与函数模板之推导规则辨析

为啥模板参数推导在 C++ 中不起作用?

深入理解函数模板

模板函数与带有自动参数的命名 lambda

模板别名、变量模板和自动类型推导无法推导模板参数