【问题标题】:I realized this is wrong but I could not delete it我意识到这是错误的,但我无法删除它
【发布时间】:2019-02-06 15:42:36
【问题描述】:

作者:这当然做不到。从下面的答案中学习。

在 C++ 中我们如何做到以下几点

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

带有函数指针?

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

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

我可以使用 lambda:

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

但是如果没有 lambda,我不知道该怎么做。有什么想法吗?

【问题讨论】:

  • 不要使用 typedef :char(*x)(unsigned) = y;。虽然我不明白为什么这是可取的。这与使用typedef int type; type x = y; 并询问如何在一行中实现该定义相同。
  • 您真的想在 Francois 解释问题时将 typedef 和变量声明合并到一行中吗?
  • 你可以一直坚持auto x = &the_function;'
  • 函数指针变量的名称出现在返回类型和参数之间它不会像type name = value;
  • @dave:函数也一样,你不需要和号。 @emma:为什么这在 C++ 中不可能?这应该可以正常工作((char) 除外,它应该是 typedef 中的 char

标签: c++ lambda function-pointers


【解决方案1】:

只要你可以写一个 typedef,你也可以写一个没有 typedef 的变量声明,语法几乎相同。

例子:

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

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

去掉typedef 关键字,你就有了一个变量声明。如果需要,可以对其进行初始化。

【讨论】:

  • 在 C 中工作得一样好,也可以用于数组类型:char (*img)[width][3] = malloc(height*sizeof(*img)); 虽然这种语法在 C++ 中毫无用处(没有运行时大小的数组类型),但它在 C 中运行良好并使用相同的违反直觉的语法。
【解决方案2】:

你可以使用auto:

auto fptr = &f;

它跳过了 typedef 的需要并保留了一个很好的语法。

【讨论】:

  • 我比接受的答案更喜欢这个答案。尽管尚不清楚 OP 是否拥有或想要 C++11 解决方案,但它更像是 C++11。但是,嘿,C++11 不再是“全新的异国”标准了。
【解决方案3】:

它几乎和 Lambdas 一样,但我认为很难阅读:

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);

【讨论】:

    【解决方案4】:

    但是如果没有 lambda,我不知道该怎么做。有什么想法吗?

    不要使用 lambda,而是使用函数:

    typedef char(*FP)(unsigned);   
    
    char foo(unsigned){ return 0;}
    
    int main() {
        FP x = foo;
    }
    

    函数指针类型定义相当讨厌,如果你可以更好地使用using

    using FP = char(*)(unsigned);
    

    Live Demo

    【讨论】:

    • 顺便说一句,我仍然不明白为什么您认为这是不可能的,而且恕我直言,您应该在问题中包含minimal reproducible example 以及错误消息,尽管我不想开始相同的讨论; )
    • 感谢您的努力,但这不是答案,请看我的答案。 max66 也有一个更优雅的变体,也请参阅他的答案。
    • @ChefGladiator“这不是答案”?!?我曾多次要求澄清但没有成功,如果我的意思是我可以回答:“这不是一个明确的问题”,无论如何你找到了你要找的东西,所以一切似乎都很好......
    【解决方案5】:

    嗯...如果你使用 lambdas,你也可以使用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 与 lambda 一起使用,就像在 FP 中使用它一样

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

    导致一些不同的事情:上面的y 的类型是带有operator() 的未命名类,而不是指向operator() 的函数指针类型。

    如果您想要一个指向函数的指针,您必须使用运算符 + 将 lambda 转换为它,您可以使用以下 static_assert() 进行验证

    auto y = +[]() ->bool { return true; };
    
    static_assert( std::is_same<decltype(y), bool(*)()>::value, "!" );
    

    【讨论】:

    • 几乎是正确的,聪明而简单..但这是一个两行:)
    • @ChefGladiator - 抱歉:我误解了你的问题;不,我认为不可能定义一个函数,并在同一个语句中,将它保存在一个函数指针变量中而不使用 lambda(你的解决方案是在一行中,但从这个意义上说,你可以写很多一行中的程序)。对于 lambda... 我已经编辑了您的编辑以显示如何使用 lambda 和 auto 获取函数指针。
    【解决方案6】:

    非常感谢所有有用的 cmets 的生动过山车。我在 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的bug,我今天报告了。

    【讨论】:

    • one line 的这个定义看起来很奇怪。因为根据它的定义,您可以通过删除所有换行符将任何数百万行程序转换为“一行”。
    • @PeterT 你了解解决方案吗?
    • @ChefGladiator 是的,这与@max66 和@user463035818 的答案完全相同。您刚刚删除了函数定义char foo(unsigned){ return ' '; } 和指针声明/赋值auto x = foo; 之间的换行符。他们还是分开的。无论您是否在它们之间放置换行符都不会改变任何东西。您可以从程序中删除所有换行符,它仍然是同一个程序
    • @pet 您当时为什么不对这个提议的答案投票?不要害怕对无用的贡献(比如这个)投反对票。
    【解决方案7】:

    代码:

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

    如果y 是捕获变量的 lambda 表达式,则无法使用当前 C++ 编译器进行编译。

    // 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 编译器,确实通过执行运行时代码生成来支持此类分配。

    【讨论】:

      猜你喜欢
      • 2018-05-02
      • 1970-01-01
      • 2019-05-29
      • 2017-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-06
      相关资源
      最近更新 更多