【问题标题】:C++ lambda ambiguous callC++ lambda 模糊调用
【发布时间】:2018-06-01 03:51:02
【问题描述】:

我正在尝试拥有两个 eachPixel 函数。一个返回图像,另一个不返回任何内容。即使我指定了 lambda 的返回类型,我也会收到“对 eachPixel 的调用不明确”。

如何解决歧义?

// this one does not return an image
void eachPixel(const QImage &image, const std::function <void (uint)>& pixelFunc, const QRect &bounds=QRect()) {
...

    for (int y=region.y(); y<=region.bottom(); y++) {
        for (int x=region.x(); x<=region.right(); x++) {
            pixelFunc(image.pixel(x,y));
        }
    }
}

// This one returns an image
QImage eachPixel(const QImage &image, const std::function <uint (uint)>& pixelFunc, const QRect &bounds=QRect()) {
...

    QImage out(image.size(), image.format());
    for (int y=region.y(); y<=region.bottom(); y++) {
        for (int x=region.x(); x<=region.right(); x++) {
            out.setPixel(x,y, pixelFunc(image.pixel(x,y)));
        }
    }
    return out;
}

void test_pixelFunc() {
    QImage image(300,200, QImage::Format_ARGB32);
    image.fill(Qt::blue);

    QImage out = eachPixel(image, [] (uint p) -> uint { //uint specified!!
        return qRgb(qRed(p), qBlue(p), qGreen(p)); // swap green and blue channels
    }, QRect (0,0, 300, 200));
    out.save("test_pixelfunc.png");


    int accumulator=0;
    eachPixel(image, [&accumulator](uint p) -> void { // void specified!
        accumulator++;
    }, QRect (0,0, 300, 200));
    qDebug() << "accumulator" << accumulator;

};

【问题讨论】:

  • 我认为问题是返回 uint 的 lambda 可以绑定到 std::function&lt;void(uint)&gt;,因为可以忽略返回值
  • 标签调度是区分这些函数的标准方法。
  • 你打算在通用代码中使用这些函数吗(每个函数都可以根据类型使用)?如果没有,那么给他们不同的名字是一个最简单的解决方案。

标签: c++ lambda


【解决方案1】:

您可以在传入的函数的返回类型上使用模板和 SFINAE。

#include <iostream>
#include <functional>

template <typename T, std::enable_if_t<std::is_same_v<void, decltype(std::declval<T>()(1))>, int> = 0>
void foo (T f) {
    // std::function<void(int)> func = f;
    // if you really need a std::function
    f(1);
}

template <typename T, std::enable_if_t<std::is_same_v<int, decltype(std::declval<T>()(1))>, int> = 0>
int foo (T f) {
    return f(1);
}

int main() {
    foo([](int x) { std::cout << "void " << x << '\n'; });

    foo([](int x) { std::cout << "int\n"; return x; });
}

对于 c++11,您可以直接使用 std::is_samestd::enable_if

template <typename T, typename std::enable_if<std::is_same<void, decltype(std::declval<T>()(1))>::value, int>::type = 0>
void foo (T f) {
    f(1);
}

template <typename T, typename std::enable_if<std::is_same<int, decltype(std::declval<T>()(1))>::value, int>::type = 0>
int foo (T f) {
    return f(1);
}

【讨论】:

  • 所以这与我想要的一致,但是 clang 告诉我错误:命名空间 std' 中没有名为 enable_if_t' 的模板。我对 SFINAE 比较陌生。我也包含了
  • 它需要 c++17。它只是一个方便的别名,因此很容易重写为 c++11/14。当我回到电脑前,我可以发布一个 c++11 版本。
  • 用 c++17 标志编译它。谢谢!如果您将示例更新为与 11 或 14 兼容的内容,那就太好了!这是唯一导致我使用该标志的原因。
  • 这太棒了!但是现在我想在 lambda 中添加两个参数:foo(1,2,3) 并且正在忽略候选模板:替换失败 [with T = (lambda no matching function for call to object of type '(lambda at .. ..
  • @UserOneFourTwo 然后您可以在enable_if 部分添加参数。 decltype(std::declval&lt;T&gt;()(1)) => decltype(std::declval&lt;T&gt;()(1, 2, 3)).
【解决方案2】:

您可以将忽略的参数添加到其中一个 lambda 以帮助解决歧义:

struct noreturn_tag {};
void foo( std::function<void(int,noreturn_tag)>);
void foo( std::function<int(int)>);

int main() {
    foo( []( int ) { return 0; } );
    foo( []( int, noreturn_tag ) {} );
}

live example

【讨论】:

    猜你喜欢
    • 2011-01-16
    • 1970-01-01
    • 1970-01-01
    • 2012-06-23
    • 1970-01-01
    • 2011-12-12
    • 2010-10-27
    • 2021-07-16
    • 1970-01-01
    相关资源
    最近更新 更多