lambda可以有"C"链接吗?

Jam*_*nze 10 c++ visual-c++ c++11

标题或多或少都说明了一切.我有以下一点代码:

#include <vector>
#include <string>
#include <iterator>
#include <algorithm>

struct xloper12;

class Something
{
public:
    std::string asString() const;
};
extern std::vector<Something> ourSomethings;

class ExcelOutputLoader
{
public:
    void load( std::vector<std::string> const& value );
    xloper12* asXloper() const;
};

extern xloper12* ProcessException( std::string const& functionName );

extern "C" __declspec(dllexport) xloper12*
getSomethingList()
{
    try {
        std::vector<std::string> results;
        results.reserve( ourSomethings.size() );
        std::transform(
            ourSomethings.begin(),
            ourSomethings.end(),
            std::back_inserter(results),
            []( Something const& o ) { return o.asString(); } );
        ExcelOutputLoader out;
        out.load( results );
        return out.asXloper();
    } catch (...) {
        return ProcessException( "GetSomthing" );
    }
}
Run Code Online (Sandbox Code Playgroud)

我用虚拟声明替换了大多数非标准头文件; 问题出在最后一个函数中(设计为从Excel调用).基本上,当使用Visual Studios 2012编译时,我收到以下警告:

falseWarning.cc(34) : warning C4190: '<Unknown>' has C-linkage specified, but re
turns UDT 'std::basic_string<_Elem,_Traits,_Alloc>' which is incompatible with C

        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>,
            _Alloc=std::allocator<char>
        ]
Run Code Online (Sandbox Code Playgroud)

(重复四次,好的措施).但据我了解,lambda定义了一个具有operator()成员的类,而不是函数.并且(§7.5/ 4)"在确定类成员名称和类成员函数的函数类型的语言链接时,忽略AC语言链接." 这意味着extern "C"应该忽略lambda.

这不是一件大事:它只是一个警告,它很容易解决(让extern "C"函数调用C++函数来完成实际工作).但我仍然想知道:有什么基本的东西,我不了解lambda,或者是开发Visual C++的人不理解它.(在后一种情况下,我很担心.由于可移植性不是问题,我们已经开始集中使用lambda.但是如果编译器的作者不理解它,那么我很担心.)

编辑:

一些更多的测试.如果我写的东西如下:

extern "C" __declspec(dllexport) void
funct1()
{
    extern std::string another();
}
Run Code Online (Sandbox Code Playgroud)

我也得到了警告.这次,我会说这是正确的. another 命名空间作用域中的函数,它在extern "C"块内声明,因此它应该具有"C"链接.(有趣的是,我也得到了一个警告,我可能会被最棘手的解析问题所困扰. extern应该已经足够让编译器意识到我没有尝试定义局部变量.)

另一方面,如果我写的东西如下:

extern "C" __declspec(dllexport) void
funct2()
{
    class Whatever
    {
    public:
        std::string asString() { return std::string(); }
    };
    std::string x = Whatever().asString();
}
Run Code Online (Sandbox Code Playgroud)

没有警告.在这种情况下,编译器会正确地忽略成员函数上指定的"C"链接.

这让我有点奇怪.编译器是否将lambda视为具有operator()函数的类(应该如此),还是将其视为函数?它看起来像后者,这让我担心如果没有其他微妙的问题,可能只有在捕获时才可见(并且可能只在非常特殊的情况下).

eca*_*mur 4

该标准似乎未明确规定这一点。

5.1.2:

3 - [...] 闭包类型在包含相应lambda 表达式的最小块作用域、类作用域或命名空间作用域中声明。[...] 5 - lambda 表达式
的闭包类型具有公共内联函数调用运算符 [...] 6 -不带lambda 捕获的 lambda表达式 的闭包类型具有公共非虚拟非-显式 const 转换函数到指向与闭包类型的函数调用运算符具有相同参数和返回类型的函数的指针。此转换函数返回的值应是函数的地址,该函数在调用时具有与调用闭包类型的函数调用运算符相同的效果。

7.5:

4 - [...] 在链接规范中,指定的语言链接适用于在链接规范中声明的所有函数声明符的函数类型、具有外部链接的函数名称以及具有外部链接的变量名称。[...] 在确定类成员名称和类成员函数的函数类型的语言链接时,AC 语言链接被忽略。[...]

因此,函数调用运算符或函数到函数指针的转换都没有 C 语言链接,因为它们是类成员函数;但由于 5.1.2p6 没有指定转换函数返回的函数在何处声明,因此其类型可能具有 C 语言链接。

一方面,如果我们考虑 7.5p4 中的示例:

extern "C" {
  class X {
  // ...
  void mf2(void(*)()); // the name of the function mf2 has C++ language
                       // linkage; the parameter has type pointer to
                       // C function
  };
}
Run Code Online (Sandbox Code Playgroud)

这表明,只要 C 函数类型是在转换声明中内联声明的,或者以其他方式在 extern“C”块内声明的,则到函数指针的转换将具有指向 C 函数的返回类型指针:

extern "C" {
  class Y {
    (*operator void())(); // return type pointer to C function
  };
}
Run Code Online (Sandbox Code Playgroud)

另一方面,要求该函数具有与函数调用操作符相同的效果,如果C语言链接不允许这样做,则这是不可能的;我们可以得出结论,该函数必须在 extern“C” 块之外声明,并且类似地在转换函数的返回类型上声明。但这可能会给编译器编写者带来额外的工作量。