【发布时间】:2021-05-07 04:47:19
【问题描述】:
我试图使用 C++20 概念创建一个静态接口,下面的代码似乎可以完成这项工作:
template <class FOO>
concept FooConcept = requires {
static_cast<void (FOO::*)(int)>(&FOO::operator());
static_cast<void (FOO::*)(char)>(&FOO::operator());
};
特别是,FOO 满足 FooConcept 类,如果此类重载 operator() 两次:分别使用 int 参数和 char 参数。
此解决方案似乎工作正常,但看起来并不美观。事实上,我更喜欢以下形式:
template <class FOO>
concept FooConcept = requires (FOO foo, int i, char c) {
{ foo(i) } -> std::same_as<void>;
{ foo(c) } -> std::same_as<void>;
};
或
template <class FOO>
concept FooConcept = std::invocable<FOO, int> && std::invocable<FOO, char>;
但是,由于隐式转换,这些方法不起作用(请参阅this post)。 有没有更好、更“语义化”的方式来表达这种约束?
【问题讨论】:
-
"FooConcept 满足 FOO 类当且仅当此类重载 operator() 两次:分别使用 int 参数和 char 参数。 - 不完全:如果但非仅当:如果
FOO具有模板运算符(例如template <typename T> void operator() (T) { })也满足 -
当然,我的错!我会立即解决这个问题。但它仍然做我想做的事:它表现为一个静态接口!
-
是的,它们反映了受概念约束的模板如何实际使用该类型。例如,拥有
t.foo(0);意味着模板可以安全地执行t.foo(0)。在这种情况下,是否转换 0 是无关紧要的。您可以查看 C++0x 概念历史,以了解为什么以您尝试做的方式指定接口的原始方向不是当前概念所采用的方向。这些会议和讨论的结果将比我更能说明原因。 -
好吧,你总是可以给丑陋起个名字:godbolt.org/z/TG45c5PGe
-
@fdev,我没有引用任何特定的东西,但我知道概念的原始化身有相当的历史。对于一个大功能走向死胡同,然后以完全不同的形式回归,这涉及到大量的讨论,所以它更像是一个一般性的参考。我并没有试图大惊小怪,而是指出当前的化身旨在为类型的用户而不是实现者建模,因为心理模型完全不同。