gpt4 book ai didi

c++ - 完美转发可变模板参数

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:35:06 25 4
gpt4 key购买 nike

我为通用信号/槽系统编写了以下实现:

template< typename... Args >
class Signal : NonCopyable
{
public:

typedef std::function< void (Args...) > Delegate;

void connect(const Delegate& delegate);
void operator ()(Args&&... args) const;

private:

std::list< Delegate > _delegates;
};

template< typename... Args >
void Signal< Args... >::connect(const Delegate& delegate)
{
_delegates.push_front(delegate);
}

template< typename... Args >
void Signal< Args... >::operator ()(Args&&... args) const
{
for (const Delegate& delegate : _delegates)
delegate(std::forward< Args >(args)...);
}

之后,我使用以下简单案例测试了我的类(class):

Signal< int > signal;

// Case 1
signal(0);

//Case 2
int i(0);
signal(i);

案例 1 编译没有问题。另一方面,情况 2 在 GCC 4.7.2 下生成以下错误:

/home/pmjobin/Workspace/main.cpp:1196:10: error: cannot bind ‘int’ lvalue to ‘int&&’
/home/pmjobin/Workspace/main.cpp:82:6: error: initializing argument 1 of ‘void Signal<Args>::operator()(Args&& ...) const [with Args = {int}]’

我知道这个问题与完美转发有关(以及我对后者的误解)。但是,我从 std::make_shared() 和 std::make_tuple() 实现中获得了灵感,但我没有看到将可变参数转发给代表的方式有任何区别。唯一值得注意的区别是 make_shared() 和 make_tuple() 都是函数模板而不是类模板,例如上面的 Signal 实现。

- 编辑 -

为了回应各种评论,这里提供了一个新版本的 Signal 类实现,它没有出现上述问题。此外,现在可以使用连接函数返回的不透明 token 断开委托(delegate)。结果可能不如其他实现(例如 boost::signal)灵活和强大,但至少,它具有简单和轻量级的优点。

template< typename Signature >
class Signal : NonCopyable
{
public:

typedef std::function< Signature > Delegate;

class DisconnectionToken
{
DisconnectionToken(typename std::list< Delegate >::iterator it)
: _it(it)
{}

typename std::list< Delegate >::iterator _it;

friend class Signal;
};

DisconnectionToken connect(const Delegate& delegate);
void disconnect(DisconnectionToken& token);

template< typename... Args >
void operator ()(Args&&... args) const;

private:

std::list< Delegate > _delegates;
};

template< typename Signature >
typename Signal< Signature >::DisconnectionToken Signal< Signature >::connect(const Delegate& delegate)
{
_delegates.push_front(delegate);
return DisconnectionToken(_delegates.begin());
}

template< typename Signature >
void Signal< Signature >::disconnect(DisconnectionToken& token)
{
if (token._it != _delegates.end())
{
_delegates.erase(token._it);
token._it = _delegates.end();
}
}

template< typename Signature >
template< typename... Args >
void Signal< Signature >::operator ()(Args&&... args) const
{
for (const Delegate& delegate : _delegates)
delegate(std::forward< Args >(args)...);
}

最佳答案

问题是您将模板参数明确指定为 int。正如 Nawaz 提到的,Args&&... 被扩展为 int&& 并且您无法将左值绑定(bind)到右值引用。

完美转发起作用的原因是,当您调用一个函数(例如)而没有指定模板参数时,它们会被推断为 &&& 然后引用崩溃(如果您不知道引用崩溃是什么,请阅读有关引用崩溃的内容)。不过,您确实明确指定了它,因此您禁止引用崩溃并将其全部搞砸。

你可以做的一件事是给 operator() 它自己的模板参数列表:

template<typename... Args2>
void operator ()(Args2&&... args) const;

...

template< typename... Args >
template< typename... Args2 >
void Signal< Args... >::operator ()(Args2&&... args) const
{
for (const Delegate& delegate : _delegates)
delegate(std::forward< Args2 >(args)...);
}

通过这种方式,您可以让引用折叠为您处理。

关于c++ - 完美转发可变模板参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14473085/

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