Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 带默认参数的函数上的SFINAE-自由函数vs运算符()_C++_C++11_Language Lawyer_Sfinae - Fatal编程技术网

C++ 带默认参数的函数上的SFINAE-自由函数vs运算符()

C++ 带默认参数的函数上的SFINAE-自由函数vs运算符(),c++,c++11,language-lawyer,sfinae,C++,C++11,Language Lawyer,Sfinae,我一直在研究它如何处理带有默认参数的函数。令我惊讶的是,对于自由函数和操作符(),结果是不同的: 模板 自动功能(F)->取消类型(F(42)) { INTA=51; 返回f(51); } 模板 自动功能(F)->取消类型(F(42,42)) { int a=0; int b=10; 返回f(a,b); } int defaultFree(int a,int b=0) { 返回a; } 自动默认lambda=[](int a,int b=0) { 返回a; }; int foo() { 返回函数

我一直在研究它如何处理带有默认参数的函数。令我惊讶的是,对于自由函数和
操作符()
,结果是不同的:

模板
自动功能(F)->取消类型(F(42))
{
INTA=51;
返回f(51);
}
模板
自动功能(F)->取消类型(F(42,42))
{
int a=0;
int b=10;
返回f(a,b);
}
int defaultFree(int a,int b=0)
{
返回a;
}
自动默认lambda=[](int a,int b=0)
{
返回a;
};
int foo()
{
返回函数(无默认值);
//返回func(defaultLambda);
}

上面的
func(defaultFree)
版本可以编译,而
func
两个模板都可用。正如预期的那样,它选择第二个参数,因为默认参数不被视为函数签名的一部分。实际上,删除第二个
func
模板会导致编译错误

但是,
func(defaultLambda)
由于模棱两可而无法执行:两个
func
模板都匹配。删除任何一个都会使此版本编译

(如果您使用类似的
操作符()手动编写
结构
,当然也会发生同样的情况。最新的
gcc
clang
MSVC
也都同意。)


对于
操作符()
,默认参数被认为是在未赋值的SFINAE上下文中,而不是自由函数中的原因是什么?

当您将自由函数作为参数传递时,它会经历函数到指针的转换。当这种情况发生时,默认参数()就消失了。现在它是一个指针,指向一个包含两个参数的函数,因此只有一个SFINAE检查可以传递给它

lambda的类型没有经过这样的调整。未计算的表达式必须包含
运算符()
的重载解析,该重载解析将查找带有默认参数的声明,从而允许在带有单个参数的调用中使用该声明


当无约束的lambda被迫转换为函数指针(例如,代码> FUNC(+ Debug Tlambda);,@ YSC的礼貌)时,歧义消失,原因相同。

< P>函数名称不是C++对象的名称。< /P> 相反,当您使用函数名时,会发生一系列转换。重载解析基于调用或(隐式或显式)转换上下文完成,并生成指针

函数的默认参数是重载解析的一部分。它们永远不会作为函数指针类型的一部分传递

您可以创建一个简单的包装器,将函数名转换为函数对象:

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  -> decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }

#define OVERLOADS_OF(...) \
  [](auto&&...args) \
  RETURNS( __VA_ARGS__( decltype(args)(args)... ) )
使用此选项,您可以修改代码:

return func(OVERLOADS_OF(defaultFree));
并获取
func
和SFINAE要考虑的默认参数,以导致歧义

现在,
OVERLOADS\u OF(defaultFree)
是一个函数对象,SFINAE测试它的参数是否可以传递给一个名为
defaultFree
的可调用对象。这允许传递1个参数

函数对象不是函数。lambda是一个函数对象,它是
重载的返回类型。可以传入函数对象并考虑其重载的
操作符();他们可以记住自己的默认参数、do SFINAE等

所以当你通过lambda时,这两种可能性都是合法的。当您传递一个函数时,它将成为指向该函数的无默认参数调用的函数指针,并且该函数明确不接受1个参数


要解决你的问题,你应该让一个过载看起来比另一个好

一种方法是使用

namespace impl {
  template <typename F>
  auto func(F f,...) -> decltype(f(42))
  {
    int a = 51;
    return f(51);
  }

  template <typename F>
  auto func(F f, int) -> decltype(f(42, 42))
  {
    int a = 0;
    int b = 10;
    return f(a, b);
  }
}
template <typename F>
auto func(F f) -> decltype( impl::func(f, 0) )
{
  return impl::func(f, 0);
}
namespace impl{
模板
自动功能(F,…)->decltype(F(42))
{
INTA=51;
返回f(51);
}
模板
自动函数(F,int)->decltype(F(42,42))
{
int a=0;
int b=10;
返回f(a,b);
}
}
模板
autofunc(F)->decltype(impl::func(F,0))
{
返回impl::func(f,0);
}
诀窍是当你通过
0
时,
int
优先于

您还可以更加明确,生成诸如“可以使用1个参数调用”、“可以使用2个参数调用”之类的特征,然后声明仅当可以使用1个参数而不是2个参数调用时才启用1 arg大小写

还有基于标签调度的过载解析排序技术。

+1。我总是假设(例如)
void f(inta=0){…}
本质上与
void f(inta){…}
加上
void f(){f(0);}
,但哇,显然不是!
namespace impl {
  template <typename F>
  auto func(F f,...) -> decltype(f(42))
  {
    int a = 51;
    return f(51);
  }

  template <typename F>
  auto func(F f, int) -> decltype(f(42, 42))
  {
    int a = 0;
    int b = 10;
    return f(a, b);
  }
}
template <typename F>
auto func(F f) -> decltype( impl::func(f, 0) )
{
  return impl::func(f, 0);
}