使用 std::function 重载解析

2024-05-10

考虑这个代码示例:

#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++ 的错误/功能?


在 C++11 中...

我们看一下构造函数模板的规范std::function(需要任何 Callable):[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应该CopyConstructible. f应该Callable(20.10.11.2) 对于参数类型ArgTypes和返回类型R。的复制构造函数和析构函数A不得投掷 例外情况。

8 后置条件: !*this如果满足以下任一条件:

  • f is a NULL函数指针。
  • f is a NULL指向成员的指针。
  • F是函数类模板的实例,并且!f

9 否则,*this目标副本f初始化为std::move(f)。 [此处留下注释]

10 Throws:f是一个函数指针或reference_wrapper<T>对于一些T。否则可能会抛出bad_alloc或抛出的任何异常F的复制或移动构造函数。

现在,构造或尝试构造(用于重载解析)std::function<void(int)> from a [](){}(即带有签名void(void))违反了要求std::function<void(int)>的构造函数。

[必需响应]/1

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

因此,据我所知,即使重载决策的结果也是未定义的。因此,g++/libstdc++ 的两个版本在这方面都是符合的。


在 C++14 中,这已更改,请参阅LWG 2132 http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2132。现在,转换构造函数模板std::function需要 SFINAE 拒绝不兼容的 Callables(下一章将详细介绍 SFINAE):

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

7 要求: F应该CopyConstructible.

8 Remarks:这些构造函数不应参与重载 决议除非f对于参数类型是可调用的 (20.9.11.2)ArgTypes...和返回类型R.

[...]

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

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

和一个调用表达式,例如

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

然后第二次过载foo明确选择:因为std::function<F>定义F作为其与外部的接口,F定义传入哪些参数类型std::function。然后,必须使用这些参数(参数类型)调用包装的函数对象。如果一个double被传递到std::function,它不能传递给采用std::string,因为没有转换double -> std::string。 对于第一次过载foo,论证[](std::string){}因此不被视为可调用std::function<void(double)>。构造函数模板已停用,因此没有可行的转换[](std::string){} to std::function<void(double)>。该第一重载被从用于解析调用(C)的重载集中移除,仅留下第二重载。

请注意,由于以下原因,上述措辞略有变化LWG 2420 http://cplusplus.github.io/LWG/lwg-defects.html#2420: 有一个例外,如果返回类型R of a std::function<R(ArgTypes...)> is void,那么上面提到的构造函数模板中的 Callable 的任何返回类型都会被接受(并丢弃)。例如,两者[]() -> void {} and []() -> bool {}可以调用std::function<void()>。因此,以下情况会产生歧义:

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

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

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


SFINAE 在这方面可以提供什么帮助?

请注意,当 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.);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 std::function 重载解析 的相关文章

  • 将 new 与 decltype 一起使用

    T t T is an implementation detail t new T want to avoid naming T to allow for flexibility t new decltype t error cannot
  • 在现代 C++ 中,临时生命周期延长何时有用?

    在 C 中 您可以将函数的返回值 返回值 而不是引用 绑定到 const 引用 并且代码仍然有效 因为该临时对象的生命周期将延长到作用域末尾 例如 std string get string return abc void f const
  • 将完整模板参数值映射到原始类型

    我想将数字映射到类型 在这个例子中 我将创建一个函数 将 sizeof 结果映射到有符号的原始类型 我想知道是否有更好的方法来完成我在现代 C 中所做的事情 即采用模板化值并将其转换为类型 现在 这可以将大小转换为已知类型 但我似乎无法在标
  • 如何使用 SOAP 且不使用 WSE 在 .NET 中签署 Amazon Web 服务请求

    亚马逊产品广告 API 以前称为 Amazon Associates Web Service 或 Amazon AWS 实施了一项新规则 即自 2009 年 8 月 15 日起 向其发送的所有 Web 服务请求都必须经过签名 他们在其网站上
  • 检测到堆栈崩溃

    我正在执行我的 a out 文件 执行后 程序运行一段时间 然后退出并显示消息 stack smashing detected a out terminated Backtrace lib tls i686 cmov libc so 6 f
  • Gwan C#,如何获取HTTP标头?

    我需要它来重写 url 以了解我正在处理哪个友好的 url 用于用户代理和其他东西 EDIT public class Gwan MethodImplAttribute MethodImplOptions InternalCall exte
  • 使用 LINQ 更新 IEnumerable 对象的简单方法

    假设我有一个这样的业务对象 class Employee public string name public int id public string desgination public int grade List
  • 增强精神、递归和堆栈溢出

    为什么下面的代码在运行时崩溃 它会给出堆栈溢出错误 include
  • MFC:如何设置CEdit框的焦点?

    我正在开发我的第一个简单的 MFC 项目 但我正在努力解决一个问题 想要设置所有的焦点CEdit其中一个对话框中的框 我的想法是 当打开对话框时 焦点位于第一个编辑框上 然后使用 选项卡 在它们之间交换 我看到了方法SetFocus 但我无
  • C#6 中的长字符串插值行

    我发现 虽然字符串插值在应用于现有代码库的字符串 Format 调用时非常好 但考虑到通常首选的列限制 字符串对于单行来说很快就会变得太长 特别是当被插值的表达式很复杂时 使用格式字符串 您将获得一个可以拆分为多行的变量列表 var str
  • 为什么 Cdecl 调用在“标准”P/Invoke 约定中经常不匹配?

    我正在开发一个相当大的代码库 其中 C 功能是从 C P Invoked 的 我们的代码库中有很多调用 例如 C extern C int stdcall InvokedFunction int 使用相应的 C DllImport CPlu
  • 从BackgroundWorker线程更新图像UI属性

    在我正在编写的 WPF 应用程序中 我有一个 TransformedBitmap 属性 该属性绑定到 UI 上的 Image 对象 每当我更改此属性时 图像就会更新 因此显示在屏幕上的图像也会更新 为了防止在检索下一张图像时 UI 冻结或变
  • 从浏览器访问本地文件?

    您好 我想从浏览器访问系统的本地文件 由于涉及大量安全检查 是否可以通过某种方式实现这一目标 或使用 ActiveX 或 Java Applet 的任何其他工作环境 请帮帮我 要通过浏览器访问本地文件 您可以使用签名的 Java Apple
  • 在 OpenGL 中渲染纹理 1 到 1

    所以我想做的是使用 OpenGL 和 C 将纹理渲染到平面上 作为显示图像的一种方式 但是我需要确保在渲染纹理时没有对纹理进行任何处理 抗锯齿 插值 平滑 模糊等 这是 OpenGL 处理渲染纹理的默认方式吗 或者是否需要设置一些标志才能禁
  • CUDA 8 编译错误 -std=gnu++11

    我正在尝试转换一些代码以使用 CUDA 并且我认为我遇到了兼容性问题 我们使用CMake 这些是我使用的 gcc 和 CUDA 版本 gcc version gcc Ubuntu 5 4 0 6ubuntu1 16 04 5 5 4 0 2
  • 在 C#.NET 中安全删除文件

    在我正在做的一个项目中 我想为用户提供 安全 删除文件的选项 例如 用随机位或 0 覆盖它 在 C NET 中是否有一种简单的方法可以做到这一点 效果如何 你可以调用系统内部删除 http technet microsoft com en
  • 如何在 winforms 应用程序的主屏幕显示之前显示欢迎屏幕?

    我想在应用程序启动时加载欢迎屏幕 然后用户单击欢迎屏幕上的按钮 然后关闭欢迎屏幕 最后显示主屏幕 static void Main startup method being called Application EnableVisualSt
  • 通过 Tab 键浏览 XML 文档字段

    In VB NET you can move through the fields in the XML member documentation with the Tab key 这在 C 中不起作用 还有其他方法吗 除了用鼠标将光标放在
  • 使用 using 声明时,非限定名称查找如何工作?

    根据 C 标准 这是格式错误还是格式良好 namespace M struct i namespace N static int i 1 using M i using N i int main sizeof i Clang 拒绝它 GCC
  • 为什么匹配模板类上的部分类模板特化与没有模板匹配的另一个部分特化不明确?

    这个问题可能很难用标题中的句子来描述 但这里有一个最小的例子 include

随机推荐