gpt4 book ai didi

c++11 - 完美转发导致错误我不明白

转载 作者:行者123 更新时间:2023-12-02 06:17:36 26 4
gpt4 key购买 nike

这是 this question 的后续行动我被鼓励在可变参数模板 operator()(...) 实现中使用完美转发。这是我的观察者模式,我想用它来调用带有可变参数的自由函数和成员函数:

#ifndef _SIGNALS_H_
#define _SIGNALS_H_

#include <utility>

/** Interface for delegates with a specific set of arguments **/
template<typename... args>
class AbstractDelegate
{
public:
virtual void operator()(args&&...) const = 0;
virtual ~AbstractDelegate() {}
};

/** Concrete function delegate that discards the function's return value **/
template<typename ReturnType, typename... args>
class FnDelegate : public AbstractDelegate<args...>
{
public:
/** member function typedef **/
using Fn = ReturnType(*)(args...);

/** constructor **/
FnDelegate(Fn fn)
: fn_{fn}
{
}

/** call operator that calls the stored function **/
void operator()(args&&... a) const override
{
(*fn_)(std::forward<args>(a)...);
}

private:
/** function pointer **/
const Fn fn_;
};

/** forward declaration **/
template<typename... args>
class Connection;

/** Signal class that can be connected to**/
template<typename... args>
class Signal
{
public:
/** connection pointer typedef **/
typedef Connection<args...>* connection_p;

/** constructor **/
Signal()
: connections_(NULL),
blocked_(false)
{
}

/** call operator that notifes all connections associated with this Signal.
The most recently associated connection will be notified first **/
void operator()(args&&... a) const
{
// only notify connections if this signal is not blocked
if (!blocked())
{
auto c = connections_;
while(c != NULL)
{
(*c)(std::forward<args>(a)...);
c = c->next();
}
}
}

/** connect to this signal **/
void connect(connection_p p)
{
p->next_ = connections_;
connections_ = p;
p->signal_ = this;
}

/** disconnect from this signal.
Invalidates the connection's signal pointer
and removes the connection from the list **/
void disconnect(connection_p conn)
{
// find connection and remove it from the list
connection_p c = connections_;
if (c == conn)
{
connections_ = connections_->next();
conn->next_ = NULL;
conn->signal_ = NULL;
return;
}
while(c != NULL)
{
if (c->next() == conn)
{
c->next_ = conn->next();
conn->next_ = NULL;
conn->signal_ = NULL;
return;
}
c = c->next();
}
}

/** block events from this signal **/
void block()
{
blocked_ = true;
}

/** unblock events from this signal **/
void unblock()
{
blocked_ = false;
}

/** is this signal blocked? **/
bool blocked() const
{
return blocked_;
}

/** destructor. disconnects all connections **/
~Signal()
{
connection_p p = connections_;
while(p != NULL)
{
connection_p n = p->next();
disconnect(p);
p = n;
}
}

connection_p connections() const {return connections_;}

private:
connection_p connections_;
bool blocked_;
};

/** connection class that can be connected to a signal **/
template<typename... args>
class Connection
{
public:
/** template constructor for static member functions and free functions.
allocates a new delegate on the heap **/
template<typename ReturnType>
Connection(Signal<args...>& signal, ReturnType (*Fn)(args...))
: delegate_(new FnDelegate<ReturnType, args...>(Fn)),
signal_(NULL),
next_(NULL),
blocked_(false)
{
signal.connect(this);
}

/** get reference to this connection's delegate **/
AbstractDelegate<args...>& delegate() const
{
return *delegate_;
}

/** call this connection's delegate if not blocked **/
void operator()(args&&... a) const
{
if (!blocked())
{
delegate()(std::forward<args>(a)...);
}
}

/** get pointer to next connection in the signal's list **/
Connection* next() const
{
return next_;
}

/** is this connection connected to a valid signal? **/
bool connected() const
{
return (signal_ != NULL);
}

/** block events for this connection **/
void block()
{
blocked_ = true;
}

/** unblock events for this connection **/
void unblock()
{
blocked_ = false;
}

/** is this connection blocked? **/
bool blocked() const
{
return blocked_;
}

/** desctructor. If the signal is still alive, disconnects from it **/
~Connection()
{
if (signal_ != NULL)
{
signal_->disconnect(this);
}
delete delegate_;
}

const Signal<args...>* signal() const {return signal_;}

friend class Signal<args...>;
private:
AbstractDelegate<args...>* delegate_;
Signal<args...>* signal_;
Connection* next_;
bool blocked_;
};

/** free connect function: creates a connection (static member or free function) on the heap
that can be used anonymously **/
template<typename ReturnType, typename... args>
Connection<args...>* connect(Signal<args...>& signal, ReturnType (*fn)(args...))
{
return new Connection<args...>(signal, fn);
}

#endif // _SIGNALS_H_

我正在尝试以这些方式使用它:

Signal<int> sig;

void print(int i)
{
std::cout << "print(" << i << ")" << std::endl;
}

int get(int i)
{
return i;
}

int main()
{
connect(sig, print);
sig(3);
int i = 4;
sig(i); // <-- here I get an error
sig(get(5));
}

我得到的错误是

main.cpp: In function ‘int main()’:
main.cpp:21:10: error: cannot bind ‘int’ lvalue to ‘int&&’
sig(i);
^
In file included from main.cpp:2:0:
main.h:89:10: error: initializing argument 1 of ‘void Signal<args>::operator()(args&& ...) const [with args = {int}]’
void operator()(args&&... a) const
^

当我使用 const int& 时错误消失了无处不在,即 Signal<const int&> sigvoid print(const int&) ,但我不明白为什么。此外,传递 const bool& 会让人感觉很尴尬。在“标志”信号的情况下。

您能否建议一个可以在此处提供更多灵 active 的修复方法?

最佳答案

您的代码审查失败了。通用引用技术不适用于 virtual抽象接口(interface)。

不要使用 Args&&在签名中,使用 Args... ,您的纯虚拟界面。在实现内部,最后一次(或唯一一次)使用 arg 使用 std::forward<Args>(args)...当且仅当参数类型是 && 时有条件地移动右值引用或文字。

当您使用 Args&&... 时,您的函数采用左值引用或右值引用。在许多情况下,您都不需要显式传递参数类型。在类型推导上下文中 Args&&...而是自动检测您的参数类型的“最佳”匹配,但您并没有推断这些类型。

当你取 Args...在类型推导上下文中,推导的类型始终是文字。通常这是次优的。但是,如果您指定类型,则没有问题。

std::forward的使用有条件地移动变量。如果传入的类型是右值引用或文字,它会移动。这恰好在完美转发通用引用和我上面描述的用例中做正确的事情。

template<typename... args>
class AbstractDelegate
{
public:
virtual void operator()(args...) const = 0;
virtual ~AbstractDelegate() {}
};

/** Concrete function delegate that discards the function's return value **/
template<typename ReturnType, typename... args>
class FnDelegate : public AbstractDelegate<args...>
{
public:
/** member function typedef **/
using Fn = ReturnType(*)(args...);

/** constructor **/
FnDelegate(Fn fn)
: fn_{fn}
{
}

/** call operator that calls the stored function **/
void operator()(args... a) const override
{
(*fn_)(std::forward<args>(a)...); // last (&only) use of each arg, ok to forward
}

...

Signal , 你可以删除 forward (保留 && 因为它是一个非 virtual 接口(interface)),或者你可以做一些更复杂的事情,只有 forward在最后一个信号上:

// stay args&&... here:
void operator()(args&&... a) const {
// only notify connections if this signal is not blocked
if (!blocked())
{
auto c = connections_;
while(c)
{
auto c_next = c->next();
if (c_next)
(*c)(a...);
else
(*c)(std::forward<args>(a)...); // last use, can forward
c = c_next;
}
}
}

作为forward是有条件的举动,在大多数情况下,您应该只在 move 的上下文中使用它将是有效的。 (当然,由于移动的条件性质,不完全是,但模式是有效的)。

因为它对 move 无效来自一个变量不止一次,它对 forward 无效来自一个变量不止一次。

关于c++11 - 完美转发导致错误我不明白,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23323547/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com