C++11 std::forward 一个指针
C++11 std::forward a pointer
我的应用程序中有一个 Signal
class,它为 classes 提供了一个公开事件的选项(与在 .NET 中一样)。
class 有效,一切正常。
昨天看了,熟悉了std::forward
。
我决定尝试在我的代码中使用它,所以我将每个 std::function<void(Args...)>
更改为 std::function<void(Args&&...)>
并且在 raise 函数(operator()
)中我使用了我看到的相同逻辑在上面 link 所以现在函数采用 Args&&...args
并且回调使用 std::forward<Args>(args)...
这是我的 Signal class 的简化版本(进行了一些更改以使其成为一个很好的示例):
template<typename... Args> class Signal
{
public:
int operator+=(const std::function<void(Args&&...)>& func) {
int token = getNewToken();
m_subscribers.emplace(token, func);
return token;
}
void operator()(Args&&... args) {
for (auto it : m_subscribers) {
it.second(std::forward<Args>(args)...);
}
}
private:
std::map<int, std::function<void(Args&&...)>> m_subscribers;
};
int main() {
int* six = new int(6);
int seven = 7;
Signal<int*> e1;
e1 += [](int* x) { std::cout << *x; };
Signal<int> e2;
e2 += [](int x) { std::cout << x; };
e1(&seven);
e2(6);
e1(six); //Error C2664 'void Signal<int *>::operator ()(int *&&)':
// cannot convert argument 1 from 'int *' to 'int *&&'
e1(std::move(six)); //This is a workaround
return 0;
}
我看到的问题是 classes(或本例中的 main
)试图用指针引发事件,我不确定如何解决这个问题。
我的主要目标是让信号 class 通用 API 如果开发人员选择使用 Signal<int*>
我不想 him\her 提高std::move
.
我做错了什么?
如果 T
是非 cv 限定的函数模板参数,则 T&&
只是一个通用引用。在您的电话接线员中:
void operator()(Args&&... args) {
Args
不是函数的模板参数,它是 class 的模板参数。因此,对于 Signal<int*>
,此 operator()
采用对 int*
的右值引用。由于 six
是左值,因此失败。
您想要的是向 Signal
提供正确的参考资格。像这样:
template<typename... Args>
class Signal
{
using F = std::function<void(Args...)>; // NB: Just Args...
public:
int operator+=(F func) {
int token = getNewToken();
m_subscribers.emplace(token, std::move(func));
return token;
}
void operator()(Args... args) { // NB: just Args...
for (auto& it : m_subscribers) { // NB: auto&
it.second(args...);
}
}
private:
std::map<int, F> m_subscribers;
};
请注意,转发 Args...
无论如何都是有问题的。如果您有两个订阅者怎么办?一旦你转发了 args 一次,你就不能真正第二次使用它们。
以上内容将使 Signal<int*>
达到您的期望。 operator()
将只接受一个 int*
,您可以将左值或右值传递给它。
Barry 的回答是正确的,但可能解释得不够清楚。
&&
仅作为转发(或"universal")参考在发生模板参数推导时 给予特殊处理。但是这里没有扣除:
Signal<int*> e1; // `Args...` is explicitly `int*`
...
e1(six); // `Args...` *has already been specified*
当模板 classes 被实例化时,它们本质上被转换为正常的 classes,只是恰好由编译器编写。请参阅 this answer 以了解用 C++ 代码编写时可能会是什么样子的示例。
在 C++14 中,没有 方法来触发 class 模板的模板参数推导而无需辅助 函数(不是构造函数):
template <typename Args...>
Signal<Args...> make_signal(Args&&...) { return Signal<Args...>; }
...但请注意,在您的情况下,这没有任何意义:您不想在创建 [=14= 时 推断 参数的类型],你想提前指定它们。
(请注意,在 C++17 中, 将 支持 template argument deduction of class templates。我认为这意味着可以 forward
template-class 参数,虽然我不是很清楚这样做的含义是什么。)
您想要允许的是在调用时转发参数。这实际上相当简单:
template<typename... Args> class Signal
{
public:
// .... skipping some code...
template <typename... CallArgs>
void operator()(CallArgs&&... args) {
callback(std::forward<CallArgs>(args)...);
}
};
.....但是,再一次,在你的情况下,这不太有意义,正如 Barry 的回答中所指出的那样。如果您有多个回调,您不想转发参数,以防止移动和重新使用它们。
可以通过检查 m_subscribers
的大小来解决这个问题,如果它是 1
则只使用 forward
ing 代码,否则按原样传递参数。但是,这可能会导致令人困惑的行为,因为调用回调的方式通常不应取决于 Signal
对象的状态。因此,您也许可以编写一个单独的 class、SingletonSignal
,用于 必须 使用 forward
ed 参数调用的回调(例如,如果回调需要转让不可复制对象(例如 unique_ptr
)的所有权。
我的应用程序中有一个 Signal
class,它为 classes 提供了一个公开事件的选项(与在 .NET 中一样)。
class 有效,一切正常。
昨天看了std::forward
。
我决定尝试在我的代码中使用它,所以我将每个 std::function<void(Args...)>
更改为 std::function<void(Args&&...)>
并且在 raise 函数(operator()
)中我使用了我看到的相同逻辑在上面 link 所以现在函数采用 Args&&...args
并且回调使用 std::forward<Args>(args)...
这是我的 Signal class 的简化版本(进行了一些更改以使其成为一个很好的示例):
template<typename... Args> class Signal
{
public:
int operator+=(const std::function<void(Args&&...)>& func) {
int token = getNewToken();
m_subscribers.emplace(token, func);
return token;
}
void operator()(Args&&... args) {
for (auto it : m_subscribers) {
it.second(std::forward<Args>(args)...);
}
}
private:
std::map<int, std::function<void(Args&&...)>> m_subscribers;
};
int main() {
int* six = new int(6);
int seven = 7;
Signal<int*> e1;
e1 += [](int* x) { std::cout << *x; };
Signal<int> e2;
e2 += [](int x) { std::cout << x; };
e1(&seven);
e2(6);
e1(six); //Error C2664 'void Signal<int *>::operator ()(int *&&)':
// cannot convert argument 1 from 'int *' to 'int *&&'
e1(std::move(six)); //This is a workaround
return 0;
}
我看到的问题是 classes(或本例中的 main
)试图用指针引发事件,我不确定如何解决这个问题。
我的主要目标是让信号 class 通用 API 如果开发人员选择使用 Signal<int*>
我不想 him\her 提高std::move
.
我做错了什么?
T
是非 cv 限定的函数模板参数,则 T&&
只是一个通用引用。在您的电话接线员中:
void operator()(Args&&... args) {
Args
不是函数的模板参数,它是 class 的模板参数。因此,对于 Signal<int*>
,此 operator()
采用对 int*
的右值引用。由于 six
是左值,因此失败。
您想要的是向 Signal
提供正确的参考资格。像这样:
template<typename... Args>
class Signal
{
using F = std::function<void(Args...)>; // NB: Just Args...
public:
int operator+=(F func) {
int token = getNewToken();
m_subscribers.emplace(token, std::move(func));
return token;
}
void operator()(Args... args) { // NB: just Args...
for (auto& it : m_subscribers) { // NB: auto&
it.second(args...);
}
}
private:
std::map<int, F> m_subscribers;
};
请注意,转发 Args...
无论如何都是有问题的。如果您有两个订阅者怎么办?一旦你转发了 args 一次,你就不能真正第二次使用它们。
以上内容将使 Signal<int*>
达到您的期望。 operator()
将只接受一个 int*
,您可以将左值或右值传递给它。
Barry 的回答是正确的,但可能解释得不够清楚。
&&
仅作为转发(或"universal")参考在发生模板参数推导时 给予特殊处理。但是这里没有扣除:
Signal<int*> e1; // `Args...` is explicitly `int*`
...
e1(six); // `Args...` *has already been specified*
当模板 classes 被实例化时,它们本质上被转换为正常的 classes,只是恰好由编译器编写。请参阅 this answer 以了解用 C++ 代码编写时可能会是什么样子的示例。
在 C++14 中,没有 方法来触发 class 模板的模板参数推导而无需辅助 函数(不是构造函数):
template <typename Args...>
Signal<Args...> make_signal(Args&&...) { return Signal<Args...>; }
...但请注意,在您的情况下,这没有任何意义:您不想在创建 [=14= 时 推断 参数的类型],你想提前指定它们。
(请注意,在 C++17 中, 将 支持 template argument deduction of class templates。我认为这意味着可以 forward
template-class 参数,虽然我不是很清楚这样做的含义是什么。)
您想要允许的是在调用时转发参数。这实际上相当简单:
template<typename... Args> class Signal
{
public:
// .... skipping some code...
template <typename... CallArgs>
void operator()(CallArgs&&... args) {
callback(std::forward<CallArgs>(args)...);
}
};
.....但是,再一次,在你的情况下,这不太有意义,正如 Barry 的回答中所指出的那样。如果您有多个回调,您不想转发参数,以防止移动和重新使用它们。
可以通过检查 m_subscribers
的大小来解决这个问题,如果它是 1
则只使用 forward
ing 代码,否则按原样传递参数。但是,这可能会导致令人困惑的行为,因为调用回调的方式通常不应取决于 Signal
对象的状态。因此,您也许可以编写一个单独的 class、SingletonSignal
,用于 必须 使用 forward
ed 参数调用的回调(例如,如果回调需要转让不可复制对象(例如 unique_ptr
)的所有权。