【问题标题】:Why is a function with default-assigned argument(s) not accepted as 0-arg Generator?为什么不接受具有默认分配参数的函数作为 0-arg 生成器?
【发布时间】:2020-09-01 20:02:39
【问题描述】:

以下代码尝试使用ns::generateInt() 作为Generator 的参数std::generate_n()

// main.cpp

#include <vector>
#include <algorithm>
#include <ctime>

static int _tmp = ( srand( time( NULL ) ), 0 );

namespace ns
{

  int generateInt( int offset = 0 )
  {
    return offset + rand() % 128;
  }

}

int main( int argc, char* argv[] )
{
  std::vector<int> v;
  int tmp = ns::generateInt(); // Fine to call ns::generateInt() w/ 0 args

  std::generate_n( std::back_inserter( v ),
                   5,
                   ns::generateInt ); // Not accepted as 0-arg Generator
  return 0;
}

此代码生成以下编译错误:

$ g++ --version && g++ ./main.cpp
g++ (GCC) 9.2.1 20190827 (Red Hat 9.2.1-1)
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

In file included from /usr/include/c++/9/algorithm:62,
                 from ./main.cpp:4:
/usr/include/c++/9/bits/stl_algo.h: In instantiation of ‘_OIter std::generate_n(_OIter, _Size, _Generator) [with _OIter = std::back_insert_iterator<std::vector<int> >; _Size = int; _Generator = int (*)(int)]’:
./main.cpp:26:36:   required from here
/usr/include/c++/9/bits/stl_algo.h:4493:18: error: too few arguments to function
 4493 |  *__first = __gen();
      |             ~~~~~^~

我知道std::generate_n() 签名接受一个接受0 个参数的Generator 函数/函子,但ns::generateInt() 的参数有一个默认值;如上所示,在非模板代码中,我可以不带参数调用ns::generateInt()

如果对上述代码所做的唯一更改是这样...

  int generateInt( /*int offset = 0*/ )
  {
    return /*offset +*/ rand() % 128;
  }

...然后它编译就好了:

$ g++ ./main.cpp
$

从编译器的角度来看这里发生了什么?我的默认参数生成器函数是 invokable 有 0 个参数,所以 为什么它不能用作 @ 987654332@ 参数到std::generate_n()

【问题讨论】:

    标签: c++ stl generator default-arguments


    【解决方案1】:

    仅仅因为generateInt()有一个默认参数值并不会改变它有一个参数的事实,你可以在错误消息中看到:

    _Generator = int (*)(int)

    默认参数值不是函数类型的一部分。

    当您直接调用generateInt() 时,编译器知道generateInt() 的完整声明,因此它知道它可以允许您省略默认参数值。

    但是,在std::generate_n() 内部,编译器不知道_Generator __gen 输入参数指向您的generateInt() 函数。它只知道__gen 指向某个函数,该函数的类型接受int 参数,任何关于默认值的知识都会丢失。由于std::generate_n() 没有在对__gen() 的调用中提供该参数值,因此会出现编译器错误。

    std::generate_n()Generator 模板参数需要一个不带参数的Callable 类型。因此,唯一您可以将 1 参数 generateInt() 原样与 std::generate_n() 一起使用的方式是将其包装在 0 参数 lambda 或仿函数中,例如:

    std::generate_n( std::back_inserter( v ),
                       5,
                       []{ return ns::generateInt(/*0*/); } );
    
    struct genInt
    {
        int operator()(){ return ns::generateInt(/*0*/); }
    };
    
    std::generate_n( std::back_inserter( v ),
                       5,
                       genInt() );
    

    【讨论】:

      【解决方案2】:

      来自std::generate_n的参考:

      g - 将被调用的生成器函数对象。 函数的签名应该等价于:

      Ret fun();

      注意它说 签名 必须是 Ret fun();not 参数是 invokable 有 0 个参数。

      您可以很容易地将调用包装在 lambda 中,以免更改 ns 中的代码:

      std::generate_n(std::back_inserter(v),
                      5,
                      [] { return ns::generateInt(); }); 
      

      【讨论】:

        【解决方案3】:

        默认参数不会改变函数的签名。签名仍然包含具有默认值的参数类型。如果一个函数的参数是指向另一个不需要参数的函数的指针,但该参数被赋予了一个指向一个确实带参数的函数的指针(有或没有默认值),那么你就有了类型不匹配。

        【讨论】:

        • 伟大的想法指出函数指针作为为什么这个规则适用的例子!
        猜你喜欢
        • 1970-01-01
        • 2019-10-04
        • 2021-12-27
        • 1970-01-01
        • 2017-09-19
        • 2015-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多