объявить и определить переменную указателя функции в одну строку - PullRequest
0 голосов
/ 06 февраля 2019

В C ++, как мы можем сделать следующее

// fundamental language construct        
   type name = value ; 
// for example 
   int x = y;

с указателями на функции?

 typedef (char)(*FP)(unsigned);

 // AFAIK not possible in C++
 FP x = y ;

Я могу использовать лямбда-выражения:

 FP x = []( unsigned k) -> char { return char(k); }

Но я делаюне знаю как это сделать без лямбды.Есть идеи?

Ответы [ 7 ]

0 голосов
/ 28 марта 2019

Вы можете использовать auto:

auto fptr = &f;

Это пропускает необходимость определения типа и сохраняет хороший синтаксис.

0 голосов
/ 02 апреля 2019

Код:

typedef char(*FP)(int);
FP x = y;

не может быть скомпилирован с текущими компиляторами C ++, если y является лямбда-выражением, захватывающим переменную.

// Compiles OK
FP x0 = [](int k) -> char { return char(k); };

// Fails to compile
int i = 123;
FP x1 = [=](int k) -> char { return char(k); };
FP x2 = [=](int k) -> char { return char(k+i); };
FP x3 = [&](int k) -> char { return char(k+i); };
FP x4 = [i](int k) -> char { return char(k+i); };
// error: cannot convert ‘main()::<lambda(int)>’ to ‘FP {aka char (*)(int)}’
//        in initialization

Причина, по которой он не компилируетсязаключается в том, что размер правой части присваивания x1 ... x4 больше, чем размер FP.

Для компилятора C ++ для выполнения присваиваний x1 ...x4 быть действительным, он должен генерировать код во время выполнения.Современные компиляторы C ++, такие как GCC и clang, не поддерживают это, главным образом потому, что это может привести к утечкам памяти, потому что C ++ не является языком сборки мусора.Некоторые реализации языка сборки мусора, такие как более ранние версии официального компилятора Go, поддерживают такие назначения, выполняя генерацию кода во время выполнения.

0 голосов
/ 28 марта 2019

Всякий раз, когда вы можете написать typedef, вы также можете написать объявление переменной без typedef с почти идентичным синтаксисом.

Пример:

 // typedef
 typedef char(*FP)(unsigned);
 FP x = y ;

 // no typedef
 char(*x)(unsigned) = y;

Удалите ключевое слово typedef,и у вас есть объявление переменной.Если хотите, назовите его инициализацией.

0 голосов
/ 07 февраля 2019

Большое спасибо всем за живые американские горки за полезные комментарии.Кто-то в Reddit, где я задавал тот же вопрос под именем пользователя «TheTiefMaster», отбросил этот «один вкладыш»:

// also works as C
char whatever(unsigned k) { return char(k); } char(*F)(unsigned) = whatever;

Позвольте мне уточнить: я понимаю, что это два утверждения в одной строке.И нет, здесь нет типа, но есть указатель на одну и ту же функцию.Использование:

    auto x = whatever(65); // 'A'
    auto y = F(66); // 'B'

Тогда я подумал, что следующее сделает определение функции и объявление ее типа:

    // FP is a type of function whoever
    char whoever(unsigned k) { return 'A'; } typedef char(*FP)(unsigned) ;

Вызов того, кто ведет себя так, как ожидается

   auto w = whoever(42) ; // 'A'

FP - то, где это начинает становиться интересным.FP является типом, и, как выясняется, можно привести тип к типу.

     // using FP as a type
     // c++ style type cast
     // empty cast returns nullptr
     auto fun = FP();
     // calling fun() above crashes
     // but it is "invocable" as per C++ rules
     static_assert(std::is_invocable_v<P2F()>);

Передача любого аргумента этому приведению, работает и возвращает ненулевой адрес:

      // update: this compiles only on MSVC
      // and is a bug
      auto fun = FP(42); 
      // type of fun is char (*) (unsigned)

Вызоврезультат этого забавного сбоя, очевидно:

     // reading access violation
     fun(123) ;

Это приведение с экземпляром из любой требуемой функции работает:

    auto fun = FP(whatever); 

    // works, obviously
    fun(65) ; // 'A'

Чтобы использовать эти знания, мы будем использовать static_cast для безопасногоприведение к тому, что мы можем назвать.Приведение типов в C ++ слишком мощное, как и приведение типов в стиле C.

     // does not compile
     // fun is the wrong type and can not be called
     auto fun = static_cast<FP>(42); 

     // does compile, fun is safe to call
         auto fun = static_cast<FP>(whatever);

    // works, obviously
    fun(65) ; // 'A'

Это исследование, очевидно, еще далеко от завершения.Я продолжу с этим, в другом месте.

Обновление:

       using FP = char (*)(int) ;
       // must not compile, compiles under MSVC
       auto oops = FP(42) ;

Это ошибка в MSVC, о которой я сообщил сегодня.

0 голосов
/ 06 февраля 2019

Ну ... если вы используете лямбды, вы также можете использовать auto, поэтому

auto x = foo;

Ниже приведен пример полной компиляции с static_assert(), который проверяет полученный тип

#include <type_traits>

char foo (unsigned)
 { return ' '; }

int main ()
 {
   auto x = foo;

   static_assert( std::is_same<decltype(x), char(*)(unsigned)>::value, "!" );
 }

Использование auto с лямбдой так же, как вы использовали его с FP

auto y = []() ->bool { return true; };

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

Если вы хотите указатель на функцию, вы должны преобразовать лямбду в нее, используя оператор +, что можно проверить с помощьюследующие static_assert()

auto y = +[]() ->bool { return true; };

static_assert( std::is_same<decltype(y), bool(*)()>::value, "!" );
0 голосов
/ 06 февраля 2019

Но я не знаю, как это сделать без лямбды.Любые идеи?

Просто не используйте лямбда, а функцию:

typedef char(*FP)(unsigned);   

char foo(unsigned){ return 0;}

int main() {
    FP x = foo;
}

Определения типа указателя на функцию довольно неприятны, если вы можете лучше использовать using:

using FP = char(*)(unsigned);

Демонстрация в реальном времени

0 голосов
/ 06 февраля 2019

Это почти то же самое, что и лямбда, но трудно читать, я думаю:

void my_int_func(int x)
{
    std::cout << "ther param is: " << x << std::endl;
}

//
int main(int argc, char *argv[])
{
    void (*foo)(int) = my_int_func;
    foo(1);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...