我有许多类,每个类都定义了一个内部结构
Result
,并且都派生自Base
类。我希望基类中有一个实现方法,该方法在派生类上调用时返回派生类 Result 结构。下面的代码可以工作,但是实际调用 get_result 时的类型重复让我很困扰。
如何让编译器自动将模板参数推导为实际的 get_result 调用?
额外限制:
当前锁定到 c++17,但如果有更新标准版本的解决方案,我也有兴趣看到它们。
#include <string>
using namespace std;
class Base
{
public:
template<typename T>
typename T::Result get_result() const
{
// static_assert(std::is_base_of_v<Base, T>, "Derived not derived from BaseClass");
return T::Deserialize();
}
};
class DerivedA
: public Base
{
public:
struct Result
{
int i = 0;
};
static Result Deserialize(/*In reality there is some stream argument here*/)
{
Result r;
r.i = 42;
return r;
}
};
class DerivedB
: public Base
{
public:
struct Result
{
string s;
};
static Result Deserialize(/*In reality there is some stream argument here*/)
{
Result r;
r.s = "Yatta";
return r;
}
};
int main()
{
{
DerivedA a;
const auto res = a.get_result<DerivedA>();
}
{
DerivedB b;
const auto res = b.get_result<decltype(b)>();
}
return 0;
}
只需重写子类中的函数即可:
class Base
{
public:
template<typename T>
typename T::Result get_result() const
{
// static_assert(std::is_base_of_v<Base, T>, "Derived not derived from BaseClass");
return T::Deserialize();
}
};
class DerivedA
: public Base
{
public:
struct Result
{
int i = 0;
};
static Result Deserialize(/*In reality there is some stream argument here*/)
{
Result r;
r.i = 42;
return r;
}
Result get_result() {
return Base::get_result<DerivedA>();
}
};
class DerivedB
: public Base
{
public:
struct Result
{
string s;
};
static Result Deserialize(/*In reality there is some stream argument here*/)
{
Result r;
r.s = "Yatta";
return r;
}
Result get_result() {
return Base::get_result<DerivedB>();
}
};
这样您就不会被迫发送用户代码中的类型:
{
DerivedA a;
const auto res = a.get_result();
}
{
DerivedB b;
const auto res = b.get_result();
}
另一种方法是使用 CRTP:
template<typename T>
class Base
{
public:
typename T::Result get_result() const
{
// static_assert(std::is_base_of_v<Base, T>, "Derived not derived from BaseClass");
return T::Deserialize();
}
};
class DerivedA
: public Base<DerivedA>
{
public:
struct Result
{
int i = 0;
};
static Result Deserialize(/*In reality there is some stream argument here*/)
{
Result r;
r.i = 42;
return r;
}
};
class DerivedB
: public Base<DerivedB>
{
public:
struct Result
{
string s;
};
static Result Deserialize(/*In reality there is some stream argument here*/)
{
Result r;
r.s = "Yatta";
return r;
}
};
在此示例中,如果您需要基于运行时的多态性,您可以添加另一个非模板基类。
在 C++23 中,您可以使用显式对象根据调用该对象的静态类型来推导类型参数:
template<typename T>
typename T::Result get_result(this const T&)
// ^^^^
{
return T::Deserialize();
}
调用它就像常规模板一样进行推导:
const auto res = a.get_result();