为什么不接受具有默认分配参数的函数作为 0-arg 生成器?



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

// 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()签名采用一个Generator函数/函子,该函数/函子接受 0 个参数,但要ns::generateInt()的参数具有默认值;如上所示,在非模板化代码中,我可以在没有参数的情况下调用ns::generateInt()

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

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

。然后它编译得很好:

$ g++ ./main.cpp
$

从编译器的角度来看,这里发生了什么?我的默认参数生成器函数可以用 0 个参数调用,那么为什么它不能用作std::generate_n()Generator参数呢?

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

_Generator = int (*)(int)

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

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

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

std::generate_n()Generator模板参数需要不带参数的 Callable 类型。 因此,您可以按原std::generate_n()样使用 1 参数generateInt()的唯一方法是将其包装在 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() );

来自std::generate_n的参考:

g - 将调用的生成器函数对象。 函数的签名应等效于以下内容:

Ret fun((;

请注意,它说签名必须是Ret fun();的,而不是说参数可以用 0 个参数调用

您可以非常轻松地将调用包装在 lambda 中,以免更改ns中的代码:

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

默认参数不会更改函数的签名。签名仍包含具有默认值的参数类型。当一个函数采用的参数是指向另一个不需要参数的函数的指针,但该参数被赋予了一个指向接受参数(有或没有默认值(的函数的指针时,则类型不匹配。

相关内容

  • 没有找到相关文章

最新更新