[英]C++ interface without inheritance for template functions
There are two different classes with intersecting set of methods:有两个具有交叉方法集的不同类:
class A {
public:
int Value1() { return 100; }
char Value2() { return "a"; }
void actionA() { };
}
class B {
public:
int Value1() { return 200; }
char Value2() { return "b"; }
void actionB() { };
}
The common part of the classes interface can be described like this:类接口的公共部分可以这样描述:
class GenericPart {
public:
virtual int Value1();
virtual char Value2();
}
Please note that classes A
and B
come from some library and therefore cannot be inherited from GenericPart
.请注意,类
A
和B
来自某个库,因此不能从GenericPart
继承。
There is a template function that works with objects implementing methods described in GenericPart
:有一个模板函数可以与实现
GenericPart
描述的方法的对象一起使用:
template <typename T>
void function(T record) {
std::cout << record.Value1() << " " << record.Value2() << std::endl;
}
Is it possible to specialize this template function to make it receive only objects that match GenericPart
?是否可以专门化此模板函数以使其仅接收与
GenericPart
匹配的对象?
You could use the C++20 feature: concepts & constraints , but a simpler solution may involve static_assert
.您可以使用 C++20 特性:概念和约束,但更简单的解决方案可能涉及
static_assert
。 See the comments in function
for some explanation有关一些解释,请参阅
function
中的注释
#include <type_traits>
#include <iostream>
class A {
public:
int Value1() { return 100; }
char Value2() { return 'a'; }
void actionA() { };
};
class B {
public:
int Value1() { return 200; }
char Value2() { return 'b'; }
void actionB() { };
};
class C { // Has the required functions but with different return types
public:
double Value1() { return 0.0; }
double Value2() { return 1.0; }
};
class D { // Has only one required function
public:
int Value1() { return 300; }
};
template <typename T>
void function(T record) {
// Check statically that T contains a `Value1()` that returns an int
static_assert(std::is_same_v<int, decltype(record.Value1())>, "int Value1() required!");
// Check statically that T contains a `Value2()` that returns an char
static_assert(std::is_same_v<char, decltype(record.Value2())>, "char Value2() required!");
std::cout << record.Value1() << " " << record.Value2() << std::endl;
}
int main()
{
A a;
B b;
C c;
D d;
function(a); // Ok
function(b); // Ok
function(c); // causes static_assert to fail
function(d); // causes static_assert to fail
}
Is it possible to specialize this template function to make it receive only objects that match GenericPart?
是否可以专门化此模板函数以使其仅接收与 GenericPart 匹配的对象?
You can't partially specialize a template function.您不能部分专门化模板函数。
But you can SFINAE enable/disable different version of it, according to the characteristicsf of T
.但是您可以根据
T
的特性 SFINAE 启用/禁用它的不同版本。
By example, you can enable function()
only for T
supporting Value1()
and Value2()
methods as follows例如,您可以仅对支持
Value1()
和Value2()
方法的T
启用function()
,如下所示
template <typename T>
auto function (T record)
-> decltype( record.Value1(), record.Value2(), void() )
{ std::cout << record.Value1() << " " << record.Value2() << std::endl; }
It's more complicated disable a function()
if doesn't support some methods (but see Timo's answer: given a has_feature_set
become trivial) so I propose to add an unused parameter of some type (by example, int
)如果不支持某些方法,则禁用
function()
更复杂(但请参阅 Timo 的回答:给定has_feature_set
变得微不足道)所以我建议添加某种类型的未使用参数(例如, int
)
template <typename T> // VVV <-- unused argument
auto function (T record, int)
-> decltype( record.Value1(), record.Value2(), void() )
{ std::cout << record.Value1() << " " << record.Value2() << std::endl; }
and develop a generic function that accept an unused type of another (but compatible) type (by example, long
) and is ever enabled并开发一个通用函数,该函数接受另一种(但兼容)类型(例如
long
)的未使用类型并且永远启用
template <typename T> // VVVV <-- unused argument
void function (T record, long)
{ /* do something with record */ }
Now you can write a first level function()
as follows现在你可以写一个第一级的
function()
如下
template <typename T>
void function (T record)
{ function(record, 0); }
Observe that you pass 0
(a int
) to the second level function()
.观察到您将
0
(a int
) 传递给第二级function()
。
This way is executed the exact match, function(T, int)
, if available (that is: if T
support the requested methods).这种方式执行精确匹配,
function(T, int)
,如果可用(即:如果T
支持请求的方法)。 Otherwise execute the generic version, function(T, long)
, that is ever available.否则,执行可用的通用版本
function(T, long)
。
Similar to max66's approach but with return type checking:类似于max66 的方法,但带有返回类型检查:
namespace detail
{
template <typename T>
std::false_type has_feature_set_helper(...); // fallback if anything goes wrong in the helper function below
template <typename T>
auto has_feature_set_helper(int) -> std::conjunction<
std::is_invocable_r<int, decltype(&T::Value1), T>, // assert that Value1 is a member function that returns something that is convertible to int
std::is_invocable_r<char, decltype(&T::Value2), T> // assert that Value2 is a member function that returns something that is convertible to char
>;
}
template <typename T>
constexpr bool has_feature_set = decltype(detail::has_feature_set_helper<T>(0))::value;
template <typename T>
auto function(T record) -> std::enable_if_t<has_feature_set<T>>
{
std::cout << record.Value1() << " " << record.Value2() << std::endl;
}
Here is a full example.这是一个完整的例子。
Note that the return type checking is not strict, meaning it's checked if the return type is convertible to the type specified in is_invocable_r_v
.请注意,返回类型检查并不严格,这意味着检查返回类型是否可转换为
is_invocable_r_v
指定的类型。
You could design an intermediate template class derived from GenericPart
.您可以设计一个从
GenericPart
派生的中间模板类。 This template you then specialize for A and B.然后,您可以专门针对 A 和 B 使用此模板。
template<class T>
class GenericPartTemplate : public GenericPart
{
public:
int Value1() override;
char Value2() override;
}
Next it is no longer needed to make your function a template:接下来不再需要使您的函数成为模板:
void function(GenericPart& record) {
std::cout << record.Value1() << " " << record.Value2() << std::endl;
}
If you want to enforce only using A and B you can use type_traits to check in compile time GenericPartTemplate is only used with those types.如果您只想强制使用 A 和 B,您可以使用type_traits来检查编译时 GenericPartTemplate 仅用于这些类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.