2014-01-08 2 views
10

Название более или менее говорит обо всем. У меня следующий бит кода:Может ли лямбда иметь связь «С»?

#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). В основном, при компиляции с визуальной студией 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(), а не a функция. И (§7.5/4) «Связывание языка C игнорируется в , определяющем языковое связывание имен членов класса и типа функции функций члена класса». Что бы означало, что на лямбда следует игнорировать extern "C".

Это не большая вещь: это всего лишь предупреждение, и это легко работать вокруг (есть функция extern "C" вызова функции C++, который делает фактическую работу). Но я все равно хотел бы знать: есть ли что-то фундаментальное, что я не понял о лямбда, или - это люди, разрабатывающие Visual C++, которые этого не понимают. (В последнем случае я волнуюсь. Поскольку переносимость не является проблемой , мы начали интенсивно использовать лямбда. Но если автору компилятора это не понятно, то я волнуюсь.)

EDIT:

Еще несколько тестов. Если я напишу что-то вроде:

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» в функции-члене.

Который заставляет меня задаться вопросом немного. Является ли компилятор обработкой лямбда как класс с функцией operator() (как это должно быть ), или это относится к ней как к функции? Похоже, что это последний, и это заставляет меня беспокоиться, если нет других тонких проблем, которые могут быть видимы только тогда, когда есть захват (и, вероятно, только в особых случаях).

+1

Похож на ошибку компилятора для меня. Это 'getSomethingList', это' extern 'C ", и он не возвращает строку. Его спецификация привязки не должна относиться к деталям внутренней реализации. –

+0

Я согласен с мнением, что это может быть ошибка VS. Вы пробовали на VS2013? – Pedrom

+0

@IgorTandetnik Да и нет. Если я явно объявил функцию в 'extern 'C" или указатель на функцию, я бы ожидал, что они также будут 'extern' C". –

ответ

4

Это, по-видимому, указано стандартом.

5.1.2:

3 - [...] Тип закрытия объявляется в наименьшей блок охвата, области видимости класса или области видимости пространства имен, который содержит соответствующие лямбда-выражение. [...]
5 - Тип закрытия для лямбда-выражения есть оператор вызова функции общественного рядный [...]
6 - Тип закрытия для лямбда-выражения с не Lambda- захват имеет публичную не виртуальную неявную функцию преобразования const для указателя на функцию, имеющую тот же параметр и возвращаемые типы, что и оператор вызова функции типа закрытия. Значение, возвращаемое этой функцией преобразования, должно быть адресом функции, которая при вызове имеет тот же эффект, что и вызов оператора вызова типа замыкания.

7,5:

4 - [...] В рычажной-спецификации, указанный язык связь относится к типам функций всех функций declarators, имена функций с внешним связыванием, и имена переменных с внешней связью, объявленные в спецификации связи . [...] Связи языка C игнорируются при определении языковой привязки имен членов класса и типа функции функций члена класса. [...]

Таким образом, ни оператор вызова функции, ни функция преобразования в функцию указателя не имеют 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 
    }; 
} 

Это говорит о том, что преобразование функционировать указатель будет иметь указатель тип возвращаемого значения для функции С тех пор, как тип функции C объявлена ​​инлайн декларации преобразования или иным образом в пределах блока экстерном «C»:

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

с другой стороны, функция требуется иметь тот же эффект, как оператора вызова функции, которая не является возможным, если язык связь с предотвращает его; we может заключить, что функция должна быть объявлена ​​вне внешнего блока «C» и аналогичным образом - тип возврата функции преобразования. Но это может наложить дополнительную нагрузку на составителей компилятора.

+0

Ключ, кажется, является точкой 6 5.1.2. Я не знал об этом, но он ясно объясняет проблему (и я ожидал бы, что сгенерированные свободные функции имеют C-привязку). Что еще более важно: это означает, что стандарт требует, чтобы lambdas без захвата вел себя по-другому, а поведение компилятора Microsoft не связано с каким-то непониманием стандарта. Я успокоился. –

3

4 ошибки - это то, что меня оголовало.

Без гражданства lambdas имеют неявное преобразование в функции. В MSVC есть что-то вроде 4 вызывающих соглашений.

Таким образом, ваша лямбда создает 4 подписи функций в пределахextern "C" блок, по одному на соглашение о вызове. Эти сигнатуры функций поднимают extern "C" и становятся незаконными, поскольку они возвращают std::string.

Возможное исправление может заключаться в разделении корпуса с интерфейса. Либо один шаг (extern "C" прототип, затем реализовать), либо функцию extern "C" вызывают функцию externinline, которая имеет лямбда.

Другим подходом было бы создание фиктивной переменной и ее захват.

Это не operator(), который порождает ошибку, это чистые указатели функций, соответствующие подписи, которые подразумеваются чистой безгражданной лямбдой.

+0

Как указывает ectamur, дополнительные стандартные функции фактически требуются стандартом. –

+0

@JamesKanze Ну, необходимо наличие * одного * из них: MSVC обеспечивает конверсии нескольких различных условных соглашений (что довольно приятно и решает некоторые головные боли при обращении к MS API), поэтому вы получаете ошибку 4 раза над. – Yakk

Смежные вопросы