当前位置: 首页 > 知识库问答 >
问题:

使用std::函数进行重载解析

钱欣悦
2023-03-14

请考虑以下代码示例:

#include <iostream>
#include <functional>

typedef std::function<void()> func1_t;
typedef std::function<void(int)> func2_t;

struct X
{
   X (func1_t f)
   { }

   X (func2_t f)
   { }
};

int main ( )
{
   X x([](){ std::cout << "Hello, world!\n"; });
}

我确信它不应该编译,因为编译器不应该能够选择两个构造函数中的一个。g-4.7.3显示了这个预期的行为:它说重载构造函数的调用是不明确的。但是,g-4.8.2成功编译了它。

此代码在 C 11 中是否正确,或者它是此版本 g 的错误/功能?

共有2个答案

阎麒
2023-03-14

这是完全正确的。因为C11 lambda表达式(以及<code>std::function</code>包装器)创建了函数对象。函数对象的巨大优势在于,即使它们是泛型的,它们仍然是一流的对象。与普通函数模板不同,它们可以传递给函数,也可以从函数返回

可以通过继承和使用声明显式创建运算符重载集。Mathias Gaunard的以下用法演示了“重载lambda表达式”。

template <class F1, class F2>
struct overload_set : F1, F2
{
    overload_set(F1 x1, F2 x2) : F1(x1), F2(x2) {}
    using F1::operator();
    using F2::operator();
};

template <class F1, class F2>
overload_set<F1,F2> overload(F1 x1, F2 x2)
{
    return overload_set<F1,F2>(x1,x2);
}

auto f = overload(
    [](){return 1;}, 
    [](int x){return x+1;}
);

int x = f();
int y = f(2);

编辑:如果在提供的示例中替换,也许会变得更加清晰

F1 -> std::function<void()> 
F2 -> std::function<void(int)>

并看到它在gcc4.7中编译

提供模板化解决方案只是为了证明概念可以扩展到通用代码,消除歧义是可能的。

在您的例子中,当使用GCC4.7这样的旧编译器时,您可以通过显式转换来帮助,gcc会解决问题,正如您在这个实时示例中所看到的

以防万一,如果你想知道,如果你换一种方式(尝试将接受int的lambda转换为不接受参数的std::函数,等等),它将不起作用

艾意蕴
2023-03-14

在C 11中...

我们来看看< code>std::function(接受任意可调用)的构造函数模板的规范:[func.wrap.func.con]/7-10

template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);

7要求:F应为可复制的<对于参数类型<code>argtype<code>和返回类型<code>R<code>而言,code>f<code>应<code>可调用<code>(20.10.11.2)。A的复制构造函数和析构函数不应引发异常。

8后置条件:< code >!*这如果以下任何一项成立:

    < li> f是一个< code >空函数指针。 < li> f是指向成员的< code>NULL指针。 < li> F是function类模板的实例,而< code >!f

9否则,*this的目标是f的副本,初始化为std::mobile(f)

10抛出:当f是函数指针或时,不得抛出异常reference_wrapper

现在,构造或尝试构造(用于重载解析)std::函数

[res.on.必需]/1

违反函数的Requires:段落中指定的前提条件会导致未定义的行为,除非函数的Throws:段落指定在违反前提条件时引发异常。

因此,恕我直言,即使重载解析的结果也是未定义的。因此,g/libstdc的两个版本在这方面都是一致的。

在 C 14 中,这已更改,请参阅 LWG 2132。现在,需要 std::函数的转换构造函数模板来 SFINAE 拒绝不兼容的可调用对象(在下一章中将详细介绍 SFINAE):

template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);

7要求:F应为可复制的

8备注:这些构造函数不得参与重载解析,除非参数类型<code>ArgTypes…</code>和返回类型<code>R</code>可调用<code>f</code>(20.9.11.2)。

[...]

“不得参与重载解决”对应于通过SFINAE拒绝。净效果是,如果您有一组重载的函数foo

void foo(std::function<void(double)>);
void foo(std::function<void(char const*)>);

以及调用表达式

foo([](std::string){}) // (C)

然后明确选择< code>foo的第二个重载:因为< code>std::function

请注意,由于 LWG 2420,上述措辞略有变化:有一个例外,如果返回类型 Rstd::函数

void foo(std::function<void()>);
void foo(std::function<bool()>);

foo([]() -> bool {}); // ambiguous

重载解析规则不会尝试在不同的用户定义的转换之间进行排名,因此 foo 的两个重载都是可行的(首先),而且两者都不是更好。

请注意,当SFINAE检查失败时,程序没有格式错误,但该函数对于过载解决方案不可行。例如:

#include <type_traits>
#include <iostream>

template<class T>
auto foo(T) -> typename std::enable_if< std::is_integral<T>::value >::type
{  std::cout << "foo 1\n";  }

template<class T>
auto foo(T) -> typename std::enable_if< not std::is_integral<T>::value >::type
{  std::cout << "foo 2\n";  }

int main()
{
    foo(42);
    foo(42.);
}

同样,通过在转换构造函数上使用 SFINAE,可以使转换变得不可行:

#include <type_traits>
#include <iostream>

struct foo
{
    template<class T, class =
             typename std::enable_if< std::is_integral<T>::value >::type >
    foo(T)
    {  std::cout << "foo(T)\n";  }
};

struct bar
{
    template<class T, class =
             typename std::enable_if< not std::is_integral<T>::value >::type >
    bar(T)
    {  std::cout << "bar(T)\n";  }
};

struct kitty
{
    kitty(foo) {}
    kitty(bar) {}
};

int main()
{
    kitty cat(42);
    kitty tac(42.);
}

 类似资料:
  • 在下面的代码中,对< code>foo的第一次调用不明确,因此无法编译。 第二个在lambda前面添加了< code> ,解析为函数指针重载。 符号在这里做什么?

  • 当我尝试构建上述代码时,gcc给出了以下错误消息。 测验cpp:34:80:错误:从类型“void(派生:)()const”转换为类型“vood(派生:())(int)const”的static_ 自动函数6 = std::bind(static_cast( 我想知道是否有任何方法可以通过类(在这里成功绑定来绑定。感谢您的帮助。

  • > < li> 有什么不同吗? “保存/转移”功能的最佳方式是什么?

  • 主要内容:C++ 是如何做到函数重载的在实际开发中,有时候我们需要实现几个功能类似的函数,只是有些细节不同。例如希望交换两个变量的值,这两个变量有多种类型,可以是 int、float、char、bool 等,我们需要通过参数把变量的地址传入函数内部。在C语言中,程序员往往需要分别设计出三个不同名的函数,其函数原型与下面类似: 但在 C++中,这完全没有必要。C++ 允许多个函数拥有相同的名字,只要它们的参数列表不同就可以,这就是 函数

  • 本文向大家介绍详解JS函数重载,包括了详解JS函数重载的使用技巧和注意事项,需要的朋友参考一下 JS的函数定义可以指定形式参数名称,多多少少我们会以为js至少可以支持参数个数不同的方法重载,然而遗憾的是这仅仅是一个假象,js所有的参数都是以arguments传递过去的,这个参数类似于数组,在函数调用的时候,所有的实参都是保存在了这个数据结构里面,我们定义函数的时候所指定的形式参数其实是为这个数据结