Преобразование пересылки лямбда в указатель функции - PullRequest
0 голосов
/ 19 мая 2018

Вот две вещи, которые работают.Мы можем создать экземпляр шаблона функции переадресации, чтобы получить указатель на функцию, принимающую lvalue:

template <class T>
void f(T &&) {}

void(*p)(int &) = f; // Cool!

Мы также можем преобразовать неперехватывающую лямбда-выражение, принимающую lvalue, в указатель на функцию, принимающую lvalue:

auto l = [](auto &) { };

void (*lp)(int &) = l; // Still cool!

Но, очевидно, ни один из GCC и Clang не преобразует универсальную лямбду пересылки в указатель на функцию, принимающую lvalue:

auto l = [](auto &&) { };

void (*lp)(int &) = l; // Not cool!

Выводы GCC:

<source>:9:21: error: invalid user-defined conversion from '<lambda(auto:1&&)>' to 'void (*)(int&)' [-fpermissive]
 void (*lp)(int &) = l;
                     ^

Выводы Clang:

<source>:9:8: fatal error: no viable conversion from '(lambda at <source>:7:10)' to 'void (*)(int &)'
void (*lp)(int &) = l;
       ^            ~
<source>:7:10: note: candidate template ignored: could not match 'type-parameter-0-0 &&' against 'int &'
auto l = [](auto &&) { };
         ^

Это все, несмотря на то, что указатель на функцию-член, принимающий lvalue , может быть получен из пересылочной лямбды:

auto lmp = &decltype(l)::operator()<int &>;

template <class...>
struct check;
check<decltype(lmp)> c;

... котораявыводит тип void (<lambda(auto:1&&)>::*)(int&) const, как и ожидалось.

Я думал, что правила свертывания ссылок были присущи любому экземпляру шаблона, и ожидал, что это сработает.Есть ли в Clang и GCC ошибка или она на самом деле не предусмотрена стандартом?

1 Ответ

0 голосов
/ 19 мая 2018

TL; DR: это определенное поведение в соответствии со стандартом.У вывода аргументов шаблона есть специальное правило для вывода аргументов шаблона при получении адреса шаблона функции, позволяющее пересылать ссылки для работы, как ожидалось.Для шаблонов функций преобразования такого правила не существует.

Примечание: похоже, это просто область, для которой еще никто не написал предложение.Если кто-то напишет предложение для этого, вполне вероятно, что это может быть сделано в будущем.


С [expr.прим.ламба] :

....Для обобщенной лямбды без лямбда-захвата тип замыкания имеет шаблон функции преобразования в указатель на функцию. Шаблон функции преобразования имеет тот же изобретенный список параметров шаблона, а указатель на функцию имеет те же типы параметров, что и шаблон оператора вызова функции. Тип возврата указателя на функцию должен вести себя так, как если бы он былспецификатор decltype, определяющий тип возврата соответствующей специализации шаблона оператора вызова функции.

выделение добавлено

Указывает, что аргументы шаблона и типы параметров функциидолжны быть скопированы в формате один к одному:

// simplified version of the example in [expr.prim.lambda]/8
struct Closure {
    template <typename T>
    void operator()(T&& t) const {
        /* ... */
    }

    template <typename T>
    static void lambda_call_operator_invoker(T&& t) {
        Closure()(std::forward<T>(t));
    }

    // Exactly copying the template parameter list and function parameter types.
    template <typename T>
    using fn_type = void(*)(T&&);
    // using fn_type = void(*)(T); // this compiles, as noted later

    template <typename T>
    operator fn_type<T>() const {
        return &lambda_call_operator_invoker<T>;
    }
};

Это не скомпилирует на все три из Clang, GCC и MSVC, , что, безусловно, может удивить, так как мыожидали, что свертка ссылок произойдет по аргументу T&&.

Однако стандарт не поддерживает это.


Важные части стандарта [temp.deduct.funcaddr] (вывод аргументов шаблона с использованием адреса шаблона функции) и [temp.deduct.conv] (вывод аргументов шаблона функции преобразования).Критически, [temp.deduct.type] специально упоминает [temp.deduct.funcaddr] , но не [temp.deduct.conv] .

Некоторые термины, используемые в стандарте:

  • P - тип возврата шаблона преобразования или тип шаблона функции
  • A -тип, который мы «пытаемся преобразовать в»

Аналогично, если P имеет форму, содержащую (T), то каждый тип параметра P i соответствующегосписок типов параметров ([dcl.fct]) для P сравнивается с соответствующим типом параметра A i соответствующего списка типов параметров для A. Если P и A являются типами функцийкоторая возникла из дедукции при получении адреса шаблона функции ([temp.deduct.funcaddr]) или при выводе аргументов шаблона из объявления функции ([temp.deduct.decl]) и P i и A i - параметры списка типов параметров верхнего уровня для P и A, соответственно, P i является дополнительнымsted, если это ссылка на пересылку ([temp.deduct.call]) и A i является ссылкой lvalue, и в этом случае тип P i изменяется на шаблонтип параметра (т. е. T&& изменяется на просто T).

выделение добавлено

Это специально вызывает взятие адресашаблона функции, делая переадресацию ссылок просто работать.Нет аналогичной ссылки на шаблоны функций преобразования.

Повторное рассмотрение примера ранее, если мы изменим fn_type на void(*)(T), то есть та же самая операция, которая описана здесь в стандарте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...