(对于类型擦除,我的意思是隐藏有关类的一些或所有类型信息,有点像Boost.Any.)
我想要掌握类型擦除技术,同时也分享那些,我知道.我希望找到一些有人在他/她最黑暗的时刻想到的疯狂技巧.:)
我所知道的第一个也是最明显的,也是最常用的方法是虚函数.只需在基于接口的类层次结构中隐藏类的实现.许多Boost库都这样做,例如Boost.Any这样做是为了隐藏你的类型,而Boost.Shared_ptr这样做是为了隐藏(de)分配机制.
然后有一个函数指针指向模板化函数的选项,同时将实际对象保存在void*指针中,如Boost.Function确实隐藏了仿函数的实际类型.可以在问题的最后找到示例实现.
所以,对于我的实际问题:
你知道其他什么类型的擦除技术?如果可能的话,请提供示例代码,用例,您对它们的体验以及可能的进一步阅读链接.
编辑
(因为我不确定是否将此作为答案添加,或者只是编辑问题,我只会做更安全的问题.)
另一个很好的技术来隐藏没有虚函数或void*摆弄的东西的实际类型,是一个GMan在这里工作,与我的问题有关,这个问题究竟是如何运作的.
示例代码:
#include <iostream>
#include <string>
// NOTE: The class name indicates the underlying type erasure technique
// this behaves like the Boost.Any type w.r.t. implementation details
class Any_Virtual{
struct holder_base{
virtual ~holder_base(){}
virtual holder_base* clone() const = 0;
};
template<class T>
struct holder : holder_base{
holder()
: held_()
{} …Run Code Online (Sandbox Code Playgroud) 根据我发现的源代码,lambda表达式基本上是由编译器创建一个带有重载函数调用运算符的类和引用变量作为成员的类实现的.这表明lambda表达式的大小是变化的,并且给定足够的引用变量,其大小可以是任意大的.
一个std::function应该有一个固定的大小,但一定要能包住任何可调用的,包括相同种类的任何lambda表达式.它是如何实现的?如果std::function内部使用指向其目标的指针,那么在std::function复制或移动实例时会发生什么?是否涉及堆分配?
以下编译和运行(在Apple LLVM版本6.1.0和Visual C++ 2015下):
#include <functional>
#include <iostream>
struct s { int x; };
int main(int argc, char **argv)
{
std::function<void (s &&)> f = [](const s &p) { std::cout << p.x; };
f(s {1});
return 0;
}
Run Code Online (Sandbox Code Playgroud)
为什么分配不会std::function<void (s &&)> f = [](const s &p) { std::cout << p.x; };产生错误?接受右值引用的函数不应该与接受const左值引用的函数具有相同的签名,如果它?const从lambda声明中删除确实会产生预期的错误.
我正在研究嵌入式系统,因此代码大小是一个问题.使用标准库将我的二进制大小提高了大约60k,从40k到100k.我想使用std :: function,但我不能证明它是60k.是否有可以使用的独立实现或类似的东西?我正在使用它在c ++ 11中使用绑定变量隐式地在成员函数中强制转换lambdas.
为了安全起见,我已经在我的 DLL 调用中使用了旧式函数指针,如下所示:
// DLL
typedef int (__stdcall* ty)();
void test(ty t)
{
if (t)
{
int r = t();
....
}
}
Run Code Online (Sandbox Code Playgroud)
而我可以使用这个:
void test(std::function<int()> t)
{
}
Run Code Online (Sandbox Code Playgroud)
然而,众所周知,后者使用类型擦除。std::function不能是原始函数指针(因为它可能会传递一个具有捕获的 lambda,因此不能是原始指针)。
因此,在 Visual Studio 中,std::function当在调试模式下从可执行构建中使用时,在发布模式下使用带有包含崩溃的函数签名的 DLL 构建,反之亦然。即使是调试模式或发布模式,行为也不相同。有时它会崩溃,有时它会起作用。
是否有我们可以依赖使用的已定义运行时行为std::function?或者这是编译器根据我传递给它的内容专门用于编译的仅编译的东西,因此,不能直接假设运行时行为?
在汇编级别,必须知道预编译单元上的函数签名。据我所知,std::function 运行时实现没有明确定义。
所以,例如,当我编译时,然后
void test(std::function<int()> t)
Run Code Online (Sandbox Code Playgroud)
可以[]() -> int, [&]() -> int, [=]() -> int通过类型擦除采用任何参数,如, 等。但是当这是预编译的,因此只有运行时,什么是可以接受的?std::function 是如何实现的?使用类指针?有没有明确定义的方法?
我不是在寻找必然与 VS 相关的解决方案,而是寻找 的标准定义(std::function如果有的话)。
给定具有捕获变量的lambda函数,例如
[&x] (int y) { x += y; }
Run Code Online (Sandbox Code Playgroud)
int x当我将这个lambda传递给类型的变量时,上下文(这里是对词法上下文中任何地方定义的某个变量的引用)在哪里std::function<void(int)>?
我知道像std::vector或的动态大小存储std::string,但它们都存储固定类型的值(当然,模板参数一旦知道).但是在lambdas的情况下,这样的存储应该能够存储任何类型的值,在客户端代码的编译时已知,它将lambda分配给a std::function,但在传递函数时不知道.
我的意思是,当我们修复具体类型时std::function,我们修复了函数签名,但不修复捕获的变量/引用的类型.在场景后面必须有一些动态大小和动态类型存储.显然,这不可能是例如std::tuple因为这需要在实例化时知道类型,std::function但它们不是.所以我想知道它是如何实现的(例如在g ++中,但可能存在与此问题无关的编译器答案).
谁能解释 lambda 函数是如何在 std::function 中表示的?编译器和 std::function 是否有隐式转换用作容器?
我最近问了一个稍微不同的问题,它被标记为这个问题的重复。答案是未定义和未指定 lambda 函数的类型。我发现一些代码似乎为 lambda 函数提供了一个容器,如下所示。我还包含了 Stroustrup 引用,这似乎与 lambda 函数没有定义类型相矛盾,但说它是一个函数闭包类型。这只会进一步混淆问题。
更新:关于 function<> 实现的部分答案在这里。
感谢您的指导。
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
static vector<function<void(int)>> cbl;
static vector<function<int(int)>> cblr;
class GT {
public:
int operator()(int x) {return x;}
};
void f()
{
auto op = [](int x) {cout << x << endl;};
cbl.push_back(op);
for (auto& p : cbl)
p(1);
auto op2 = [](int x) {return x;};
cblr.push_back(op2);
cblr.push_back(GT()); …Run Code Online (Sandbox Code Playgroud) 在这个缩短的示例(不是真实世界的代码)中,我试图Callback用一个调用int &,但是,当通过该CallMethod方法时,模板参数被解释为一个int,这意味着它无法将其转换为目标参数类型.
这可能吗?我知道我可以在调用时将参数强制转换为正确的类型CallMethod,但是如果可能的话,我希望解决方案是隐式的.
#include <functional>
#include <iostream>
using namespace std;
void Callback(int &value)
{
value = 42;
}
template <typename Method, typename ...Params>
void CallMethod(Method method, Params ...params)
{
method(std::forward<Params>(params)...);
}
int main()
{
int value = 0;
CallMethod(&Callback, value);
cout << "Value: " << value << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud) 我是C++的新手,我正在学习lambdas,functor和callables,我知道有一个包装类,即std::function允许存储和调用不同类型的callables(只要具有相同的调用签名,或功能类型).
现在,我明白你可以使用函数类型参数来实现函数指针参数,如下所示:
void fun(int,int*(int,int&));
Run Code Online (Sandbox Code Playgroud)
它只不过是一个函数,它接受一个int函数指针和一个函数指针int *f(int,int&),即使该语言允许我将函数作为参数传递(带或不带符号).事实上,函数参数列表也可能写成:
void fun(int,int*(*)(int,int&));
Run Code Online (Sandbox Code Playgroud)
现在,回到std::function类型
我知道我可以std::function使用函数类型进行实例化,并允许将任何类型的可调用函数传递给包装器.但是,函数类型不是我可以在任何实例化中用作模板类型参数的类型,例如:
std::vector<int(int)> f_vec;
Run Code Online (Sandbox Code Playgroud)
相反,我应该制作一个函数指针的向量
std::vector<int(*)(int)> f_vec;
Run Code Online (Sandbox Code Playgroud)
这将允许我插入指向函数的指针,但不能插入函子或lambda.
所以,我的问题是,如何使用类型参数实例化模板,如函数类型?在库std::function类型的引擎盖下发生了什么.我的意思是一个函数类型在我看来是一个我不能在模板中使用的类型?请求你能让事情变得更加清晰,因为我刚开始学习这些话题.谢谢
我正在做一些实验,试图了解转发是如何工作的,并且我遇到了我不理解的情况.
当我用clang 3.8 -O3编译时
class Foo {
Foo(const std::string& s) : str(s) {}
std::string str;
};
Run Code Online (Sandbox Code Playgroud)
和
class Foo {
Foo(std::string&& s) : str(std::forward<std::string&>(s)) {}
std::string str;
};
Run Code Online (Sandbox Code Playgroud)
Foo foo("this is a test")在第一种情况下构造Foo 几乎快2倍.
为什么?
c++ ×10
c++11 ×7
lambda ×4
std-function ×2
templates ×2
embedded ×1
g++ ×1
stl ×1
type-erasure ×1