具有重载类型转换运算符的函数对象崩溃
function object with overloaded type conversion operator crashes
我正在实现类型擦除class(我正在研究模板和静态多态性),它可以包装任何可调用对象(任何支持函数调用语法的对象)。
阅读 C++ 模板完整指南并在网上搜索,我想出了这个实现:
// erasure.hpp
class Object
{
friend void swap(Object &, Object&); // friend declaration
private:
class Concept
{
public:
virtual ~Concept() = default;
virtual void Invoke() = 0;
virtual Concept *Clone() = 0;
protected:
Concept() = default;
};
template <typename T>
class Model : public Concept
{
public:
//Model(const T&) : mObject(object) {}
//Model(T&&) : mObject(move(object)) {}
template <typename U> // forwarding constructor (forwarding (universal) reference)
Model(U &&object) : mObject(forward<U>(object)) {}
void Invoke() override { mObject(); }
Model *Clone() override;
private:
T mObject;
};
public:
template <typename T> // forwarding constructor (forwarding (universal) reference)
Object(T &&object) : mConcept(new Model<typename remove_reference<T>::type>(forward<T>(object))) {}
Object(const Object &other) : mConcept(other.mConcept->Clone()) {} // copy constructor
Object(Object &other) : Object(static_cast<const Object&>(other)) {} // delegating constructor (inhibits forwarding constructor)
Object(Object &&other) : mConcept(other.mConcept) { other.mConcept = nullptr; } // move constructor
~Object() {delete mConcept; } // destructor
template <typename T> // forwarding assignment operator
Object &operator=(T&&);
Object &operator=(const Object&); // copy assignment operator
Object &operator=(Object &other) { return *this = static_cast<const Object&>(other); }
//Object &operator=(Object &other) { return this->operator=(static_cast<const Object&>(other)); }
Object &operator=(Object&&); // move assignment operator
void operator()();
private:
Concept *mConcept;
};
template <typename T>
Object::Model<T> *Object::Model<T>::Clone()
{
return new Model(mObject);
}
template <typename T>
Object &Object::operator=(T &&object)
{
delete mConcept;
mConcept = new Model<remove_reference_t<T>>(forward<T>(object));
return *this;
}
这非常class简洁明了,所以我省略了 cpp 实现文件。当我从自由函数、重载函数调用运算符的仿函数甚至 lambda 构造类型擦除对象时,它工作正常:
#include "erasure.hpp"
#include <iostream>
#define PRINT(msg) std::cout << (msg)
#define PRINTLN(msg) PRINT(msg) << std::endl;
void FreeFunction()
{
PRINTLN("in free function");
}
class Functor1
{
public:
void operator()()
{
PRINTLN("in functor overloaded function call operator");
}
};
class Functor2
{
private:
typedef void(*PtrToFun)();
public:
operator PtrToFun()
{
PRINTLN("in functor overloaded type conversion operator to function pointer");
}
};
int main(int argc, char **argv)
{
Object o1 = &FreeFunction;
Object o2 = Functor1();
Object o3 = Functor2();
Object o4 = []() -> void { PRINTLN("in lambda"); };
o1();
o2();
o4();
o3(); // error: program crashes
return 0;
}
但是当我使用将类型转换运算符重载为函数指针的仿函数 (Functor2) 时,程序在运算符中打印字符串后崩溃。它只是冻结和崩溃(堆栈溢出?)。
为什么?
编辑
在 rafix07 发表评论后,我编辑了代码,现在可以使用了:
class Functor2
{
private:
typedef void(*PtrToFun)();
PtrToFun p = []() { PRINTLN("in functor overloaded type conversion operator to function pointer"); };
public:
operator PtrToFun()
{
return p;
}
};
您的 Functor2
只是函数指针的包装器,因此它应该在转换运算符中使用一些指针并 return 它。没有 return
,你有 UB 因为在垃圾地址上调用函数。
要使 Functor2
接受任何带有签名 void ()
的函数,您可以这样写:
class Functor2
{
private:
typedef void(*PtrToFun)();
PtrToFun ptr;
public:
Functor2(PtrToFun p ) : ptr(p) {}
operator PtrToFun()
{
PRINTLN("in functor overloaded type conversion operator to function pointer");
return ptr;
}
};
Object o3 = Functor2(&FreeFunction);
o3();
我正在实现类型擦除class(我正在研究模板和静态多态性),它可以包装任何可调用对象(任何支持函数调用语法的对象)。
阅读 C++ 模板完整指南并在网上搜索,我想出了这个实现:
// erasure.hpp
class Object
{
friend void swap(Object &, Object&); // friend declaration
private:
class Concept
{
public:
virtual ~Concept() = default;
virtual void Invoke() = 0;
virtual Concept *Clone() = 0;
protected:
Concept() = default;
};
template <typename T>
class Model : public Concept
{
public:
//Model(const T&) : mObject(object) {}
//Model(T&&) : mObject(move(object)) {}
template <typename U> // forwarding constructor (forwarding (universal) reference)
Model(U &&object) : mObject(forward<U>(object)) {}
void Invoke() override { mObject(); }
Model *Clone() override;
private:
T mObject;
};
public:
template <typename T> // forwarding constructor (forwarding (universal) reference)
Object(T &&object) : mConcept(new Model<typename remove_reference<T>::type>(forward<T>(object))) {}
Object(const Object &other) : mConcept(other.mConcept->Clone()) {} // copy constructor
Object(Object &other) : Object(static_cast<const Object&>(other)) {} // delegating constructor (inhibits forwarding constructor)
Object(Object &&other) : mConcept(other.mConcept) { other.mConcept = nullptr; } // move constructor
~Object() {delete mConcept; } // destructor
template <typename T> // forwarding assignment operator
Object &operator=(T&&);
Object &operator=(const Object&); // copy assignment operator
Object &operator=(Object &other) { return *this = static_cast<const Object&>(other); }
//Object &operator=(Object &other) { return this->operator=(static_cast<const Object&>(other)); }
Object &operator=(Object&&); // move assignment operator
void operator()();
private:
Concept *mConcept;
};
template <typename T>
Object::Model<T> *Object::Model<T>::Clone()
{
return new Model(mObject);
}
template <typename T>
Object &Object::operator=(T &&object)
{
delete mConcept;
mConcept = new Model<remove_reference_t<T>>(forward<T>(object));
return *this;
}
这非常class简洁明了,所以我省略了 cpp 实现文件。当我从自由函数、重载函数调用运算符的仿函数甚至 lambda 构造类型擦除对象时,它工作正常:
#include "erasure.hpp"
#include <iostream>
#define PRINT(msg) std::cout << (msg)
#define PRINTLN(msg) PRINT(msg) << std::endl;
void FreeFunction()
{
PRINTLN("in free function");
}
class Functor1
{
public:
void operator()()
{
PRINTLN("in functor overloaded function call operator");
}
};
class Functor2
{
private:
typedef void(*PtrToFun)();
public:
operator PtrToFun()
{
PRINTLN("in functor overloaded type conversion operator to function pointer");
}
};
int main(int argc, char **argv)
{
Object o1 = &FreeFunction;
Object o2 = Functor1();
Object o3 = Functor2();
Object o4 = []() -> void { PRINTLN("in lambda"); };
o1();
o2();
o4();
o3(); // error: program crashes
return 0;
}
但是当我使用将类型转换运算符重载为函数指针的仿函数 (Functor2) 时,程序在运算符中打印字符串后崩溃。它只是冻结和崩溃(堆栈溢出?)。
为什么?
编辑
在 rafix07 发表评论后,我编辑了代码,现在可以使用了:
class Functor2
{
private:
typedef void(*PtrToFun)();
PtrToFun p = []() { PRINTLN("in functor overloaded type conversion operator to function pointer"); };
public:
operator PtrToFun()
{
return p;
}
};
您的 Functor2
只是函数指针的包装器,因此它应该在转换运算符中使用一些指针并 return 它。没有 return
,你有 UB 因为在垃圾地址上调用函数。
要使 Functor2
接受任何带有签名 void ()
的函数,您可以这样写:
class Functor2
{
private:
typedef void(*PtrToFun)();
PtrToFun ptr;
public:
Functor2(PtrToFun p ) : ptr(p) {}
operator PtrToFun()
{
PRINTLN("in functor overloaded type conversion operator to function pointer");
return ptr;
}
};
Object o3 = Functor2(&FreeFunction);
o3();