我试图将我的对象打包到64个字节(自行开发的属性),我需要保存getter和setter成员函数。
我真的很喜欢std :: function但是很大:
sizeof(std::function<int(void)>)
给出40个字节(VS2017,x64)。
我试图弄清楚是否有std::function
16字节大的精简版本并且内部没有额外的分配?
更新:解决方案如下所示,两个成员函数需要16个字节(x64)。 成员函数指针变成了模板参数,所以它们什么都不带,只存储对象指针和vptr。
感谢@Yakk。
class Property
{
public:
virtual ~Property() {}
virtual QVariant value() const = 0;
virtual void setValue(const QVariant& value) = 0;
};
template<typename TYPE,
typename CLASS,
TYPE(CLASS::*get)() const,
void(CLASS::*set)(const TYPE &)>
class PropertyValue : public Property
{
public:
PropertyValueGet() = delete;
PropertyValueGet(PropertyValueGet const& ) = delete;
PropertyValueGet& operator=(PropertyValueGet const& ) = delete;
PropertyValueGet(CLASS* object) : m_object(object) {}
TYPE getValue() const {
return (m_object->*get)();
}
void setValue(const TYPE& value) {
(m_object->*set)(value);
}
// Property implementation
//
virtual QVariant value() const final {
QVariant v = QVariant::fromValue<TYPE>(getValue());
return v;
}
virtual void setValue(const QVariant& value) final {
setValue(value.value<TYPE>());
}
private:
CLASS* m_object = nullptr;
};
答案 0 :(得分:4)
m_getter
的类型为decltype(m_getter)
。
在Modern C ++中使用std::mem_fn
是一个坏主意 - lambda表达式是一种更清晰,更透明(对开发人员和编译器而言)绑定参数的方式。
Stephan T. Lavavej在谈话“functional: What's New, And Proper Usage"中提到了与lambdas相比std::bind
和std::mem_fn
的一些缺点:
您应该使用 lambda表达式。 E.g:
auto my_getter = [](Foo& foo){ return foo.getter(); };
Foo some_foo;
my_getter(some_foo);
或
Foo some_foo;
auto my_getter = [&some_foo](){ return some_foo.getter(); };
my_getter();
答案 1 :(得分:3)
<div id="contatoPage">
<div id="contatoEmail" >
<div class="row">
<div class="emailForm">
<form role="form" action="Contato.php" method="post" >
<div class="form-group">
<label for="name"> Nome:</label>
<input type="text" class="form-control" id="name" name="name" required maxlength="50">
</div>
<div class="form-group">
<label for="email"> Email:</label>
<input type="email" class="form-control" id="email" name="emailCliente" required maxlength="50">
</div>
<div class="form-group">
<label for="name"> Assunto:</label>
<input type="text" class="form-control" id="name" name="assunto" required maxlength="50">
</div>
<div class="form-group">
<label for="name"> Message:</label>
<textarea class="form-control" type="textarea" name="message" id="message" placeholder="Escreva sua mensagem aqui" maxlength="6000" rows="7"></textarea>
</div>
<button type="submit" name="envEmail" class="botaoEnviar pull-right" id="btnContactUs">Enviar! →</button>
</form>
<?php
if(isset($_POST['envEmail']))
{
$nome= $_POST['name'];
$emailcliente= $_POST['emailCliente'];
$assunto= $_POST['assunto'];
$menssagem= $_POST['message'];
$to = 'jun@paconstrushop.com.br'; //can receive notification
$subject = $assunto;
$message = 'Email enviado por:'.$nome."\n".$menssagem;
$headers = 'From:'.$emailcliente. "\r\n" .
'Reply-To: jun@paconstrushop.com.br' . "\r\n" .
'X-Mailer: PHP/' . phpversion();
if(@mail($to, $subject, $message, $headers))
{
echo " <div id=success_message style=width:100%; height:100%; > <h3>Sent your message successfully!</h3> </div>";
}
else
{
echo "<div id=error_message style=width:100%; height:100%; > <h3>Error</h3> Sorry there was an error sending your form. </div>";
}
}
?>
</div>
</div>
</div>
有一个类型,&Foo::getter
,最有可能比TYPE(Foo::*)(void)
更小的类型。同样,std::function<TYPE(void)>
是&Foo::setter
。
但是你可以通过void(Foo::*)(TYPE)
放弃使用&Foo::field
的{{1}}错误概念获取甚至更小。
TYPE Foo::*
另外附带一个实例
template<typename T, typename C>
struct Property
{
Property(T C::*member) : member(member) {}
T & get(C & c) { return std::invoke(member, c); }
void set(C & c, T & t) { std::invoke(member, c) = t; }
private:
T C::* member;
}
答案 2 :(得分:2)
这是一个c++17答案,因为在c++11中执行此操作非常烦人。
这是一个“简单的”无状态视图,仅限std :: function。
// utility tag type for dispatching
template<class Tag>
struct tag_t {};
// helper type to find the "caller" function pointer used to erase invoke:
template<class Sig>
struct caller_type;
template<class R, class...Args>
struct caller_type<R(Args...)> {
using type = R(*)(void*, Args&&...);
};
template<class Sig>
using caller = typename caller_type<Sig>::type;
// make a caller<Sig> that type erases calling T with Sig:
template<class T, class Sig>
struct make_caller;
template<class T, class R, class...Args>
struct make_caller<T, R(Args...)> {
caller<R(Args...)> operator()() const {
return [](void* ptr, Args&&...args)->R {
return (*static_cast<T*>(ptr))( std::forward<Args>(args)... );
};
}
};
template<class T, class...Args>
struct make_caller<T, void(Args...)> {
caller<void(Args...)> operator()() const {
return [](void* ptr, Args&&...args)->void {
(*static_cast<T*>(ptr))( std::forward<Args>(args)... );
};
}
};
// provides operator() overload compatible with Sig,
// then dispatches the call down through a derived type D:
template<class D, class Sig>
struct call_dispatch;
template<class D, class R, class...Args>
struct call_dispatch<D, R(Args...)> {
R operator()(Args...args)const {\
auto* caller = self()->get_caller(tag_t<R(Args...)>{});
return (*caller)( self()->pvoid(), std::forward<Args>(args)... );
}
auto self() { return static_cast<D*>(this); }
auto self() const { return static_cast<D const*>(this); }
};
// stores a function pointer to invoke Sig
template<class Sig>
struct call_storage {
caller<Sig> f = nullptr;
template<class T>
static call_storage make() {
return {make_caller<T, Sig>{}()};
}
caller<Sig> get_caller( tag_t<Sig> ) const { return f; }
};
// a table of such function pointers
template<class...Sig>
struct call_vtable:call_storage<Sig>... {
template<class T>
static call_vtable make() {
return {call_storage<Sig>::template make<T>()...};
}
using call_storage<Sig>::get_caller...;
};
// overload helper to dispatch to correct Sig:
template<class D, class...Sig>
struct call_dispatcher:
call_dispatch<D, Sig>...
{
using call_dispatch< D, Sig >::operator()...;
};
// Erases invoking but not storing an arbitrary type T
// with all of ...Sig. Stores the invokers inside itself,
// not in an external vtable, to increase locality at the cost
// of per-instance size:
template<class... Sig>
struct call_view_t:
private call_vtable<Sig...>,
call_dispatcher< call_view_t<Sig...>, Sig... >
{
template<class F,
std::enable_if_t<!std::is_same<std::decay_t<F>, call_view_t>{}, bool> =true
>
call_view_t( F&& f ):
call_vtable<Sig...>( call_vtable<Sig...>::template make<std::decay_t<F>>() ),
ptr( std::addressof(f) )
{}
call_view_t()=default;
call_view_t(call_view_t const&)=default;
call_view_t& operator=(call_view_t const&)=default;
explicit operator bool() const { return ptr != nullptr; }
void* pvoid() const { return ptr; }
using call_vtable<Sig...>::get_caller;
private:
void* ptr = 0;
};
它需要2个指针,一个用于存储非拥有状态,另一个用于存储调用者(它还支持调用的任意数量的签名)。
我们可以创建一个新的call_view
类型,而不是存储ptr*
,存储一块内存;在该块中,放置new是传入的F
的状态,pvoid()
返回指向它的指针。
除非你将自己局限于琐碎的破坏和复制/移动,否则你还必须存储指向函数的指针。
由于call_impl
仅根据所存储事物的类型而不是其值而有所不同,我们可以将f
存储在vtable中,并且仅从{{继承} 1}}。在vtable中,我们可以存储call_dispatch
的副本的复制/移动/销毁。
这允许我们使用一个状态F
,只需要在std::function
中放置任何状态的存储器上需要1个指针。
请注意,上面的代码可能有拼写错误,并不能解决您的问题。它有9/10件可以解决你的问题。
std::function
并尝试// a vtable that represents "I can be copied or moved and destroyed":
struct copyable_vtable {
void(*dtor)(void*)=0;
void(*copy)(void* dest, void const* src)=0;
void(*move)(void* dest, void* src)=0;
template<class T>
static copyable_vtable make() {
return {
[](void* ptr){ static_cast<T*>(ptr)->~T(); },
[](void* dest, void const* src){
::new( dest ) T(*static_cast<T const*>(src));
},
[](void* dest, void * src){
::new( dest ) T(std::move(*static_cast<T const*>(src)));
}
};
}
};
// the vtable that our "small function" needs:
template<class...Sig>
struct small_func_vtable:
copyable_vtable,
call_vtable<Sig...>
{
template<class T>
static small_func_vtable make() {
return {
copyable_vtable::template make<T>(),
call_vtable<Sig...>::template make<T>()
};
}
template<class T>
static small_func_vtable const* get() {
static const auto vtable = make<T>();
return &vtable;
}
};
// bundles up the size and alignment requirements:
template<std::size_t S=sizeof(void*)*3, std::size_t A=alignof(void*)>
struct size_params {
enum { size = S, align = A };
};
// A small stack allocated std::function that refuses to get bigger
// If you try to construct it with something larger, you get
// a static assert failure. Also supports arbitrary number of
// overloads of ():
template<class Size, class...Sig>
struct small_function:
call_dispatcher< small_function<Size, Sig...>, Sig... >
{
private:
small_func_vtable<Sig...> const* vtable = 0;
mutable std::aligned_storage_t< Size::size, Size::align > data;
public:
template<class F,
std::enable_if_t<!std::is_same<std::decay_t<F>, small_function>{}, bool> =true
>
small_function( F&& f )
{
static_assert( sizeof(std::decay_t<F>)<=Size::size );
static_assert( alignof(std::decay_t<F>)<=Size::align );
::new( (void*)&data ) std::decay_t<F>( std::forward<F>(f) );
vtable = small_func_vtable<Sig...>::template get<std::decay_t<F>>();
}
small_function()=default;
// we could refactor this into base class:
small_function(small_function const& o) {
if (!o.vtable) return;
o.vtable->copy( pvoid(), o.pvoid() );
vtable = o.vtable;
}
small_function(small_function&& o) {
if (!o.vtable) return;
o.vtable->move( pvoid(), o.pvoid() );
vtable = o.vtable;
}
small_function& operator=(small_function const& o) {
if (this == &o) return *this;
if (vtable) {
vtable->dtor(pvoid());
vtable = nullptr;
}
if (o.vtable) {
o.vtable->copy( pvoid(), o.pvoid() );
vtable = o.vtable;
}
return *this;
}
small_function& operator=(small_function&& o) {
if (this == &o) return *this;
if (vtable) {
vtable->dtor(pvoid());
vtable = nullptr;
}
if (o.vtable) {
o.vtable->move( pvoid(), o.pvoid() );
vtable = o.vtable;
}
return *this;
}
// use null vtable to determine if we are empty:
explicit operator bool() const { return vtable != nullptr; }
// these must be visible to dispatch system. I think
// they are harmless to expose to end users, so I don't bother
// with making them private and friending dispatch system:
void* pvoid() const { return &data; }
template<class S>
caller<S> get_caller(tag_t<S> t) const {
if (!vtable) return nullptr;
return vtable->get_caller(t);
}
};
template<class...Sig>
using simple_small_function = small_function< size_params<>, Sig... >;
。
答案 3 :(得分:2)
OP澄清说使用的成员是固定的:
String allWordHelper(){
String holder = " ";
for (Map.Entry<Character, DictionaryTree> child : children.entrySet()) {
if(completeWord != null){
//holder += completeWord + child.getValue().allWordHelper();
Word_Holder.allWords.add(completeWord);
}else{
holder += child.getValue().allWordHelper();
}
}
return holder;
}
每个template<class T>
struct generic_property {
virtual T getValue() const = 0;
virtual void setValue(T const&) = 0;
protected:
virtual ~generic_property() {}
};
template<class D, class T, void(D::*set)(T const&), T(D::*get)() const>
struct property:generic_property<T> {
T getValue() const final {
return (self->*get)();
}
void setValue(T const& t) final {
(self->*set)(t);
}
property( D* s ):self(s) {}
// cannot usually safely copy/move/trivial:
property() = delete;
property( property const& ) = delete;
property& operator=( property const& ) = delete;
private:
D* self = 0;
};
struct Bob {
void setFoo( int const& i ) { std::cout << i << " set\n"; }
int getFoo() const { std::cout << 42 << " get\n"; return 42; }
property<Bob, int, &Bob::setFoo, &Bob::getFoo> foo;
Bob():foo(this) {}
};
占用1个指针空间(因此32到64位,或4-8个字节,具体取决于操作系统的位宽)。
添加property
和virtual
会占用另一个指针空间。
我们可以在generic_property<T>
中手动键入擦除,而不是虚拟继承。
答案 4 :(得分:0)
让我们看一下std::function
所需的内容:
所以,这是5个指针值=&gt; 5 * 8 = 64位Windows上的40字节,至少可以容纳成员函数指针。