gpt4 book ai didi

c++ - 为什么 std::function 不能与完美转发一起使用?

转载 作者:行者123 更新时间:2023-11-28 06:08:19 25 4
gpt4 key购买 nike

注意:

  • 我提前道歉,但我无法进一步减少/简化代码(即,我所有较小的测试都无法重现此问题)
  • 使用 Visual Studio 2010 Premium 可以编译代码的版本 1,但不能正确执行
  • 代码的版本 2 和 3 使用 Visual Studio 2010 Premium 成功编译和执行
  • 代码的所有版本都使用 ideone.com (C++14) 成功编译和执行

版本 1 输出(Visual Studio 2010)

Signal1<A1>::raise(7)
Slot::operator()(7)
Slot1<A1>::operator()(7)
Handling new value: 3931764 // This value changes on each execution

版本 2 输出(Visual Studio 2010)

Signal1<A1>::raise(7)
Slot::operator()(7)
Slot1<A1>::operator()(7)
Handling new value: 7

版本 3 输出(Visual Studio 2010)

Signal1<A1>::raise(7)
Slot::operator()(7)
Slot1<A1>::operator()(7)
Handling new value: 7

代码

#include <functional>
#include <iostream>
#include <vector>

// Version 1: uses perfect forwarding with std::function<void (T)>
// Version 2: uses perfect forwarding with std::function<void (const T&)>
// Version 3: forgoes perfect forwarding with std::function<void (T)>

#define VER 1

class Slot
{
public:
template<typename A1>
#if VER == 1 || VER == 2
void operator()(A1&& a1) const;
#elif VER == 3
void operator()(A1 a1) const;
#endif
};

template<typename A1>
class Slot1 : public Slot
{
public:
template<typename T>
Slot1(T* instance, void (T::*fn)(A1)) :
mFn(std::bind(fn, instance, std::placeholders::_1))
{
// Do nothing
}

void operator()(A1 a1) const
{
std::cout << "Slot1<A1>::operator()(" << a1 << ")\n";
mFn(a1);
}

private:
#if VER == 1 || VER == 3
std::function<void (A1)> mFn;
#elif VER == 2
std::function<void (const A1&)> mFn;
#endif
};

template<typename A1>
#if VER == 1 || VER == 2
void Slot::operator()(A1&& a1) const
#elif VER == 3
void Slot::operator()(A1 a1) const
#endif
{
std::cout << "Slot::operator()(" << a1 << ")\n";
#if VER == 1 || VER == 2
static_cast<const Slot1<A1>&>(*this)(std::forward<A1>(a1));
#elif VER == 3
static_cast<const Slot1<A1>&>(*this)(a1);
#endif
}

class Signal
{
public:
void connect(Slot* slot)
{
mSlots.push_back(slot);
}

std::vector<Slot*> mSlots;
};

template<typename A1>
class Signal1 : public Signal
{
public:
void raise(A1 a1)
{
std::cout << "Signal1<A1>::raise(" << a1 << ")\n";
(*mSlots[0])(a1);
}
};

class Model
{
public:
void setValue(int value)
{
mValue = value;
mValueChangedSignal.raise(value);
}

Signal1<int> mValueChangedSignal;

private:
int mValue;
};

class View
{
public:
void handleChange(int value)
{
std::cout << "Handling new value: " << value << "\n";
}
};

int main()
{
View view;

Slot1<int> slot(&view, &View::handleChange);

Signal1<int> signal;
signal.connect(&slot);

signal.raise(7);
return 0;
}

这是 Visual Studio 错误还是我做错了什么?

最佳答案

版本 1 和 2 执行未定义的行为,如 Slot::operator()Slot1<int>A1 调用等于int&Signal1<int> 内,然后 static_cast s 本身到 Slot1<int&> .它不是 Slot1<int&> ,因此该转换会生成一个错误的引用,然后您会使用它,然后繁荣,无论发生什么。

请检查您的类型并停止基于参数类型的显式转换,这非常不安全且难以追踪。

我对理清 #ifdef 的困惑不感兴趣s 在你的代码中确定版本 3 是否发生类似的错误。你的设计从根本上来说是不安全的,因为相对无害的参数在传递给 Slot 的参数中发生了变化。导致未定义的行为。您不应该隐式地将推导的参数传递给 operator()并用它来转换 this 的类型派生类型。

尊重类型转换。

以下是您的代码在情况 1 中导致的未定义行为的详 segmentation 类:

signal.raise(7);

通话

Signal1<int>::raise(int)

调用

void raise(int a1)
{
(*mSlots[0])(a1);
}

在这里a1int 类型的左值.所以这个调用

插槽::运算符()(int& a1) 常量因为这就是转发引用的工作方式 -- T&&通过了 int&推导T作为int& . body 然后包含

static_cast<const Slot1<int&>&>(*this)(std::forward<int&>(a1));

转换 *this引用Slot1<int&> ,一个与所讨论的对象无关的类。当您尝试与之交互时,会出现未定义的行为。

正如我所说,修复此问题是可能的,但您的根本问题是 Slot::operator() 中类型参数的推导不是一种合适的方式来确定 Slot 的子类型你想投 *this进入。传递给 operator() 的表达式类型一般而言,在调用点或推导点都不明显。如果您没有完全正确地理解这种类型,结果就是未定义的行为。这通常“似乎有效”,直到发生一些完全不相关的变化并且它崩溃了。

从基础转换为派生时,您必须非常小心、明确,并记录您在每一步所做的事情。

如果有人调用您的 Slot (这实际上是一个 Slot1<int> )具有一个无符号整数,未定义的行为。一个 size_t,未定义的行为。无符号字符,未定义的行为。一个 uint16_t,未定义的行为。将 long long 添加到 unsigned int 中,用结果调用你,未定义的行为。一种隐式转换为 int 的类型,未定义的行为。

没有类型安全的多态性很少是一个好主意。

最重要的是,您的代码中没有充分的理由这样做。 Signal1<A1>可以实现 connect而不是 Signal服用Slot1<A1> -- 毕竟,你的类型就在那里 -- 并存储一个 Slot1<A1> 的数组而不是 Slot 的数组.鉴于唯一类型的Slot以后不会导致未定义行为的是 Slot1<A1> ,为什么存储错误的类型呢?

关于c++ - 为什么 std::function 不能与完美转发一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31885254/

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