当使用lambda作为模板参数时,出现了什么编译器错误?

4

编辑:

这被报告为VS2012 C++编译器在Microsoft Connect上的错误 (链接)

2014年11月11日:微软回应说,此错误的修复应该会出现在下一个主要版本的Visual C++中。


我一直在努力解决我不理解的VS2012编译器错误消息,所以我将问题缩小到了似乎是最少的。

我正在使用VS2012构建以下main.cpp

#include <utility>

template <typename T>
struct A
{
    T x;
    A(A&& other) : x(std::move(other.x)) { }
    A(T&& x) : x(std::move(x)) { }
};

template <typename T>
A<T> build(T&& x)
{
    return A<T>(std::move(x));
}

int main(int argc, char* argv[])
{
    auto f = []()
    {
        return build([](){}); //error here
    };
    return 0;
}

要点是我正在尝试使用lambda作为build函数的模板类型T。我收到的错误消息是:

1>  main.cpp
1>C:\test\main.cpp(21): error C2664: 'A<T>::A(A<T> &&)' : cannot convert parameter 1 from 'A<T>' to 'A<T> &&'
1>          with
1>          [
1>              T=void (__cdecl *)(void)
1>          ]
1>          and
1>          [
1>              T=main::<lambda_c3c618d445b3cb24eede9bf304860ad7>::()::<lambda_4240e93016e3e420ff8383c9350ae130>
1>          ]
1>          and
1>          [
1>              T=void (__cdecl *)(void)
1>          ]
1>          Reason: cannot convert from 'A<T>' to 'A<T>'
1>          with
1>          [
1>              T=main::<lambda_c3c618d445b3cb24eede9bf304860ad7>::()::<lambda_4240e93016e3e420ff8383c9350ae130>
1>          ]
1>          and
1>          [
1>              T=void (__cdecl *)(void)
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

我已经做过研究并查看了错误信息页面(链接),但我仍然不知道问题出在哪里。你能解释一下这个编译器错误吗?

编辑

这里肯定有些奇怪。如果我更改main中的代码,让它看起来像这样:

auto f = []()
{
    int* n = new int(0);
    auto g = [=](){ return *n; };
    *n++;
    return build<decltype(g)>(std::move(g));
};

我收到一个错误提示信息,建议在调用build时 T=int (__cdecl *)(void),这意味着decltype(g)给我一个函数指针? 嗯? 我通过值捕获指针,然后修改它 - 不应该创建一个没有函数指针转换的函数对象吗? 也许我没有理解清楚。
相关内容请参见:Lambda expressions : n3290 draft
此外,如果这是VS2012编译器中的错误,您能想到一种解决方法吗?
3个回答

3

我可以确认,在Linux上使用GCC编译器,这段代码可以顺利编译。因此我认为,Visual Studio可能是错误的来源。


2

我没有 Windows 或 Visual Studio 进行验证,也没有太多在 C++ 中使用 Lambda 函数的经验,但也许您需要在函数中包含(虽然是空的)参数列表?即将第 21 行更改为:

return build([](){});

两个版本都可以使用GCC编译,但是可能Visual Studio会更加挑剔。

我可能还有一个问题,就是你在第24行定义的lambda函数是否能够正常工作,因为它的返回值涉及到函数内部定义的lambda函数。


我现在已经标准化了它,感谢你指出这一点,因为它可能会引起混乱。不过,VS2012对于无输入语法也完全接受。 - Timothy Shields
@Timothy:参数列表是可选的,最小有效的lambda表达式是[]{}。这不是问题。 - Xeo
@Xeo 我知道,最初我省略了参数列表是有效的。但是它吸引了人们对错误可能源头的注意,所以我想使用人们更熟悉的语法,以免分散注意力。 - Timothy Shields
@Anthony当你说“两个版本都能用GCC编译”时,是否意味着你已经采用了我提供的代码并成功构建了它?如果是这样的话,对我来说这是一个重要的消息,因为那很可能意味着这是一个VS2012编译器错误。我不知道如何运行GCC来检查自己-如果您知道它可以使用GCC工作,那将对我非常有帮助! - 我正在下载GCC,所以我可以尝试弄清楚如何自己进行这种测试,以备将来之需。 - Timothy Shields
@TimothyShields:是的,抱歉我没有表达得更清楚。在Linux上,无论是否带有空参数列表,它都可以编译成功。 - Anthony

0

我不知道这种行为是否符合标准,但在使用VC++ 2019时,只有在选项/permissive-下才会出现该错误,然后开启严格模式。

无论如何,以下是解决问题的方法,只需将lambda表达式强制转换为引用类型:

template <typename FUNC>
void f(FUNC& o){}

int main()
{
    f((std::function<void()>&)[](){});
    // or also:
    auto func = [](){};
    f(func);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接