我获得了一组我无法更改的课程。 (出于好奇:这些是从 Simulink 模型自动生成的)不幸的是,这些类公开了太多的东西。因此,我决定为所有这些类编写一个接口类来封装它们并使使用它们更安全一些。然而,我在编写一个适用于所有可能出现的类的接口类(模板)时遇到了麻烦。
大多数类都定义了一个名为
MyStruct
的结构,我需要在接口类中使用它。不过,出于各种原因,我不想使用该名称,而是想在接口类中使用别名。所以,我最终得到了以下代码:
// Classes to interface with
class MyClass1
{
public:
struct MyStruct
{
double f1;
double f2;
} _myVar {};
};
class MyClass2
{
public:
struct MyStruct
{
double g1;
double g2;
double g3;
} _myVar {};
};
class MyClass3
{};
// Interface class
template<typename T>
class Interface
{
public:
using MyAlias = typename T::MyStruct;
void setVar(const MyAlias& newVar) {_object._myVar = newVar;};
void doSomething()
{
MyAlias inputs {};
}
private:
T _object {};
};
// Main function
int main()
{
Interface<MyClass1> i1; // compiles fine
Interface<MyClass2> i2; // compiles fine
Interface<MyClass3> i3; // compile error: no type named ‘MyStruct’ in ‘class MyClass3’
return 0;
}
这无法编译,因为
MyClass3
没有定义类型MyStruct
。因此,无法定义别名。
我尝试仅在定义类型时才考虑定义别名和方法,并偶然发现了 SFINAE 概念。不幸的是,我无法在代码中使用标准库中的任何内容。因此,像
std::enable_if
或 std::void_t
这样的事情是不可能的。人工智能针对别名和方法提出了以下解决方案:
template<typename U, typename = void>
struct check_mystruct {using type = void; };
template<typename U>
struct check_mystruct<U, decltype((void)U::MyStruct, void())> { using type = typename U::MyStruct; };
using MyAlias = typename check_mystruct<T>::type;
template<typename U = MyAlias>
void setVar(const U& newVar) {_object._myVar = newVar;};
void setVar(...) { /* do nothing */ };
据我了解,这应该可行。仅当 check_mystruct
存在时,才应使用
S::MyStruct
结构体的部分特化和方法的模板化版本。 但是,如果我将其组合到初始程序中,如下所示:
// Classes to interface with
class MyClass1
{
public:
struct MyStruct
{
double f1;
double f2;
} _myVar {};
};
class MyClass2
{
public:
struct MyStruct
{
double g1;
double g2;
double g3;
} _myVar {};
};
class MyClass3
{};
// Interface class
template<typename T>
class Interface
{
public:
template<typename U, typename = void>
struct check_mystruct {using type = void; };
template<typename U>
struct check_mystruct<U, decltype((void)U::MyStruct, void())> { using type = typename U::MyStruct; };
using MyAlias = typename check_mystruct<T>::type;
template<typename U = MyAlias>
void setVar(const U& newVar) {_object._myVar = newVar;};
void setVar(...) { /* do nothing */ };
void doSomething()
{
MyAlias inputs {};
}
private:
T _object {};
};
// Main function
int main()
{
Interface<MyClass1> i1; // compiles fine
Interface<MyClass2> i2; // compiles fine
Interface<MyClass3> i3; // compiles fine
i1.doSomething(); // compile error: In instantiation of ‘void Interface<T>::doSomething() [with T = MyClass1]’: variable or field ‘inputs’ declared void
i2.doSomething();
i3.doSomething();
return 0;
}
我收到一个编译错误:
main.cpp: In instantiation of ‘void Interface<T>::doSomething() [with T = MyClass1]’:
main.cpp:58:19: required from here
main.cpp:44:17: error: variable or field ‘inputs’ declared void
44 | MyAlias inputs {};
| ^~~~~~
因此,即使 void
的类型为
MyClass
,别名也设置为
MyStruct
。我不明白。有人可以解释出了什么问题吗?还有其他方法可以实现我想要做的事情吗?
// Interface class
template <typename T>
class Interface
{
public:
void doSomething() {}
// ...
private:
T _object {};
};
template<typename T>
requires (requires {
typename T::MyStruct;
})
class Interface<T>
{
public:
using MyAlias = typename T::MyStruct;
void setVar(const MyAlias& newVar) {_object._myVar = newVar;};
void doSomething()
{
[[maybe_unused]]MyAlias inputs {};
}
private:
T _object {};
};