可以声明类型,只允许类而不允许任何子类(我知道这违反了Liskov substitution principle,但我仍然想知道是否有方法做到这一点)。
比如说
#include <iostream>
struct A {};
struct B : A {};
void fn(/*I want this to be only A, not subclasses*/A arg) {
// do stuff with arg
std::cout << "fn called";
}
int main() {
A a;
fn(a);
B b;
fn(b); // should raise compile-time error here
}
我希望fn(b)
给予一个编译时错误。
代码链接:https://wandbox.org/permlink/AiLkHwp5rg7AD7gf
4条答案
按热度按时间nzk0hqpo1#
你可以使用
std::same_as
的概念:46qrfjad2#
您可以删除函数
因为模板匹配所有内容,所以在重载解析中总是考虑它。由于非模板比模板更受欢迎,因此
fn(A{});
是比模板更好的候选者,另一方面,fn(B{});
与模板完全匹配,而非模板则需要隐式转换。当然,您不能禁止某人在调用之前将
B
转换为A
,因此解决方案充其量是脆弱和混乱的。bf1o4zei3#
您可以禁止其他重载:
rfbsl7qr4#
有很多方法可以做到这一点,所以让我们比较一下:
约束(C++20起)
诊断
此选项有效,因为
auto
在此处推导为B
,这与A
不同。该约束不关心B
是否可以转换为A
(尽管std::convertible_to
可以)。优缺点
std::enable_if
和SFINAE(C++11起)诊断
这个实现与C++20版本基本相同,我们只是通过SFINAE而不是约束来实现它。
优缺点
std::enable_if
的诊断 * 这个好 *,其他编译器将产生更差的输出删除函数(C++11起)
诊断
我们可以声明任何函数为deleted,调用它会使程序变得不规范。此解决方案之所以有效,是因为
fn(T)
在重载解决方面胜出,因为不需要从B
到T
的隐式转换,只需要从B
到A
。优缺点
= delete
在任意函数上是令人惊讶的,它不是一个众所周知的功能static_assert
(C++11起)诊断
这是一个非常简单但有效的解决方案。我们检查给定的类型是否实际上是
A
。不会考虑从B
到A
的隐式转换。优缺点
总结
错误的质量对于每个解决方案都是好的,至少在使用clang时是这样。您更喜欢哪种解决方案部分取决于您的需求,以及您希望与哪个版本的C++兼容。然而,个人偏好也在这里发挥作用,因为没有一个解决方案在每个方面都比其他解决方案更好。