在 C++ 中是否有来自 C# 的关键字 "where" 的类似物?
Is there an analogue of keyword "where" from C# in C++?
我需要用 C++ 创建一个模板 class。我需要确保模板参数的类型是 class,其中包含 1 个 int 字段和 1 个 string 字段(可以有更多字段,但这些是强制性的)。
例如,在 C# 中,我可以定义一个带有方法或属性的接口,如下所示:
interface MyInterface {
int GetSomeInteger();
string GetSomeString();
}
然后我可以在我的模板中使用它 class:
class MyClass<T> where T: MyInterface {}
有没有办法在 C++ 中做这样的事情?
在 当前 版本的 C++ 中最常用的方法是一种称为 "duck-typing".
的技术
它只涉及使用 T
就好像它实现了接口,如果您使用不兼容类型的 class 会让编译器失败。
template<typename T>
class MyClass<T> {
int foo() {
T val;
return val.GetSomeInteger();
}
};
class Valid {
public:
int GetSomeInteger() {return 0;}
};
class Invalid {
};
int main() {
// works fine
MyClass<Valid> a;
a.foo();
// fails to compile
MyClass<Invalid> b;
b.foo();
}
请注意,有一些方法可以更正式地执行此操作,但所涉及的代码量通常得不偿失。
C++20 有概念。一些编译器已经支持它们。例如下面的 gcc (trunk) -std=c++2a -fconcepts
:
#include <string>
#include <iostream>
#include <concepts>
template<typename T>
concept HasGetIntAndString = requires(T& a) {
{ a.GetSomeInteger() } -> std::same_as<int>;
{ a.GetSomeString() } -> std::same_as<std::string>;
};
template <HasGetIntAndString T>
void bar(const T& t){
std::cout << t.GetSomeInteger() << " " << t.GetSomeString();
}
struct foo {
int GetSomeInteger() const { return 42; }
std::string GetSomeString() const { return "some"; }
};
struct foo_not {
std::string GetSomeInteger() { return "some"; }
int GetSomeString() { return 42; }
};
int main(){
bar( foo{});
bar( foo_not{});
}
结果:
<source>: In function 'int main()':
<source>:28:19: error: use of function 'void bar(const T&) [with T = foo_not]' with unsatisfied constraints
28 | bar( foo_not{});
| ^
<source>:12:6: note: declared here
12 | void bar(const T& t){
| ^~~
<source>:12:6: note: constraints not satisfied
<source>: In instantiation of 'void bar(const T&) [with T = foo_not]':
<source>:28:19: required from here
<source>:6:9: required for the satisfaction of 'HasGetIntAndString<T>' [with T = foo_not]
<source>:6:30: in requirements with 'T& a' [with T = foo_not]
<source>:7:23: note: 'a.GetSomeInteger()' does not satisfy return-type-requirement
7 | { a.GetSomeInteger() } -> std::same_as<int>;
| ~~~~~~~~~~~~~~~~^~
<source>:8:22: note: 'a.GetSomeString()' does not satisfy return-type-requirement
8 | { a.GetSomeString() } -> std::same_as<std::string>;
| ~~~~~~~~~~~~~~~^~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
在 C++20 之前你可以使用 SFINAE。但是,通常更简单、更合适的做法是不对 tempalte 参数进行不必要的限制。如果模板确实调用了 T::GetSomeInteger()
但类型 T
没有这样的方法,则模板将在不采取任何进一步措施的情况下编译失败。 SFINAE 主要是为了提供更好的错误信息。
C++20 为您提供最接近 C# 的解决方案:
#include <concepts>
template <class T>
concept MyInterface = requires(T x)
{
{ x.GetSomeInteger() } -> std::same_as<int>;
};
然后:
template <MyInterface T>
struct MyClass
{
// ...
};
我需要用 C++ 创建一个模板 class。我需要确保模板参数的类型是 class,其中包含 1 个 int 字段和 1 个 string 字段(可以有更多字段,但这些是强制性的)。
例如,在 C# 中,我可以定义一个带有方法或属性的接口,如下所示:
interface MyInterface {
int GetSomeInteger();
string GetSomeString();
}
然后我可以在我的模板中使用它 class:
class MyClass<T> where T: MyInterface {}
有没有办法在 C++ 中做这样的事情?
在 当前 版本的 C++ 中最常用的方法是一种称为 "duck-typing".
的技术它只涉及使用 T
就好像它实现了接口,如果您使用不兼容类型的 class 会让编译器失败。
template<typename T>
class MyClass<T> {
int foo() {
T val;
return val.GetSomeInteger();
}
};
class Valid {
public:
int GetSomeInteger() {return 0;}
};
class Invalid {
};
int main() {
// works fine
MyClass<Valid> a;
a.foo();
// fails to compile
MyClass<Invalid> b;
b.foo();
}
请注意,有一些方法可以更正式地执行此操作,但所涉及的代码量通常得不偿失。
C++20 有概念。一些编译器已经支持它们。例如下面的 gcc (trunk) -std=c++2a -fconcepts
:
#include <string>
#include <iostream>
#include <concepts>
template<typename T>
concept HasGetIntAndString = requires(T& a) {
{ a.GetSomeInteger() } -> std::same_as<int>;
{ a.GetSomeString() } -> std::same_as<std::string>;
};
template <HasGetIntAndString T>
void bar(const T& t){
std::cout << t.GetSomeInteger() << " " << t.GetSomeString();
}
struct foo {
int GetSomeInteger() const { return 42; }
std::string GetSomeString() const { return "some"; }
};
struct foo_not {
std::string GetSomeInteger() { return "some"; }
int GetSomeString() { return 42; }
};
int main(){
bar( foo{});
bar( foo_not{});
}
结果:
<source>: In function 'int main()':
<source>:28:19: error: use of function 'void bar(const T&) [with T = foo_not]' with unsatisfied constraints
28 | bar( foo_not{});
| ^
<source>:12:6: note: declared here
12 | void bar(const T& t){
| ^~~
<source>:12:6: note: constraints not satisfied
<source>: In instantiation of 'void bar(const T&) [with T = foo_not]':
<source>:28:19: required from here
<source>:6:9: required for the satisfaction of 'HasGetIntAndString<T>' [with T = foo_not]
<source>:6:30: in requirements with 'T& a' [with T = foo_not]
<source>:7:23: note: 'a.GetSomeInteger()' does not satisfy return-type-requirement
7 | { a.GetSomeInteger() } -> std::same_as<int>;
| ~~~~~~~~~~~~~~~~^~
<source>:8:22: note: 'a.GetSomeString()' does not satisfy return-type-requirement
8 | { a.GetSomeString() } -> std::same_as<std::string>;
| ~~~~~~~~~~~~~~~^~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
在 C++20 之前你可以使用 SFINAE。但是,通常更简单、更合适的做法是不对 tempalte 参数进行不必要的限制。如果模板确实调用了 T::GetSomeInteger()
但类型 T
没有这样的方法,则模板将在不采取任何进一步措施的情况下编译失败。 SFINAE 主要是为了提供更好的错误信息。
C++20 为您提供最接近 C# 的解决方案:
#include <concepts>
template <class T>
concept MyInterface = requires(T x)
{
{ x.GetSomeInteger() } -> std::same_as<int>;
};
然后:
template <MyInterface T>
struct MyClass
{
// ...
};