lambda可以有“C”链接吗?

时间:2014-01-08 16:13:52

标签: 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" );
    }
}

我用虚拟替换了大多数非标准头文件 声明;问题出在最后一个函数中(即 旨在从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>
        ]

(重复四次,好的措施)。但据我所知 它,lambda定义了一个具有operator()成员的类,而不是 一个功能。并且(§7.5/ 4)“C语言链接被忽略 确定班级成员姓名的语言联系 和类成员函数的函数类型。“哪个会 意味着在lambda上应该忽略extern "C"

这不是一件大事:它只是一个警告,而且很容易上班 around(让extern "C"函数调用一个C ++函数 做实际的工作)。但我还是想知道:是吗? 我不了解lambda的基本原理,或者 是开发Visual C ++的人不理解它。 (在后一种情况下,我很担心。因为便携性不是 问题,我们已经开始密集使用lambda。但如果 作者的编译器不理解它,然后我很担心。)

编辑:

更多测试。如果我写下这样的话:

extern "C" __declspec(dllexport) void
funct1()
{
    extern std::string another();
}

我也得到了警告。这次,我会说这是正确的。 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();
}

没有警告。在这种情况下,编译器正确执行 忽略成员函数上指定的“C”链接。

这让我有点疑惑。是编译器处理 lambda作为具有operator()函数的类(因为它 应该),还是将其视为一种功能?它看起来像那样 后者,这让我担心,如果没有其他微妙的 涉及的问题,可能只有在捕获时才可见 (可能只在非常特殊的情况下)。

2 个答案:

答案 0 :(得分:4)

标准似乎没有详细说明。

5.1.2:

  

3 - [...]闭包类型在包含相应 lambda-expression 的最小块作用域,类作用域或命名空间作用域中声明。 [...]
  5 - lambda-expression 的闭包类型有一个公共内联函数调用操作符[...]
  6 - 没有 lambda-capture lambda-expression 的闭包类型具有公共非虚拟非显式const转换函数,用于指向具有相同参数的函数和返回类型作为闭包类型的函数调用操作符。此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用操作符具有相同的效果。

7.5:

  

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

因此函数调用操作符或函数指针的转换函数都没有C语言链接,因为它们是类成员函数;但由于5.1.2p6未指定 where ,因此声明了转换函数返回的函数,其类型可能具有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
  };
}

这表明转换为函数指针会返回指向C函数的返回类型指针,只要C函数类型被声明为转换声明的内联或者在extern“C”块中:

extern "C" {
  class Y {
    (*operator void())(); // return type pointer to C function
  };
}

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

答案 1 :(得分:3)

这4个错误让我明白了。

无状态lambda具有对函数的隐式转换。在MSVC中,支持4种调用约定。

因此,您的lambda在 extern "C"块中创建了4个函数签名,每个调用约定一个。这些功能签名选择extern "C"并变为非法,因为它们返回std::string

可能的修复方法可能是将主体从界面中分离出来。可以是一步(extern "C"原型,然后实现),或让您的extern "C"函数调用具有lambda的非extern inline函数。

另一种方法是创建一个虚拟变量并捕获它。

生成错误的不是operator(),它是由纯无状态lambda隐含的签名匹配纯函数指针。