如果条件不满足,调用函数时会出现编译时错误
c++: compile time error on function call if condition not satisfied?
这是我想要实现的一个简化示例。因此,这可能看起来有点傻,但请耐心听我说。假设我有
template<int i>
class Class1{
foo(){cout<<"j is divisible by i, so we will hang out"<<endl;}
}
和具有固定int j
变量的class2
:由这样的int模板化或具有成员变量。我希望class2
实例只有在满足特定条件时才能调用foo()
,在这种情况下,我想确保说(j%i==0)
。
我能想出的最好的办法是:
template<int i>
class Class1{
static const int intParam=i;
template<bool true>
foo(){cout<<"j is divisible by i, so we will hang out"<<endl;}
}
然后类2会这样调用它:
foo<class1::intParam%j>()
不太好。有更好的方法吗?我已经看到了'std::enable_if',这是相关的,但我不太确定。
如果你想要更大的图片,这是一个信号/委托代理机制。在系统中,只有当任务对象与任务中指定的角色枚举(int i
)匹配时,执行对象才能够服务/请求任务对象。本质上,这应该是没有动态多态性的基于枚举的设计。在c++中是否有更好的方法来做到这一点?
使用static_assert
:
template<int i, int j>
class Class1
{
public:
void foo()
{
static_assert(i % j == 0, "Message upon i % j == 0 being false");
cout<<"j is divisible by i, so we will hang out"<<endl;
}
};
叫它e.g.
Class1<42, 24> f;
f.foo();
更新注释,只需添加j
作为foo()
的额外模板参数,然后:
template<int i>
class Class1
{
public:
template<int j>
void foo()
{
static_assert(i % j == 0, "Message upon i % j == 0 being false");
cout<<"j is divisible by i, so we will hang out"<<endl;
}
};
int main()
{
Class1<42> f;
f.foo<24>();
return 0;
}
与其他解决方案相反,我建议使用SFINAE根据条件启用或禁用功能。
template<int i>
struct Class1 {
template<int j, std::enable_if_t<i % j == 0>* = 0>
void foo() {
}
};
这个解决方案的优点是,如果foo()
有其他重载,编译器会尝试它们,而不是给出一个硬错误。正如您在这个实例中看到的,编译器给出的错误是:
main.cpp: In function 'int main()':
main.cpp:12:24: error: no matching function for call to 'Class1<3>::foo()'
Class1<3>{}.foo<2>();
^
这意味着错误发生在用户的代码中,而不是在你的头文件中,如果有一个替代函数在这个不起作用的时候,编译器会尝试其他的重载-
编辑
你真正要求的是让写的代码是错误的,我很遗憾地说这是不可能的。我不认为有一个IDE可以防止你编写无效的代码。但是,如果您实现了下面我的解决方案之一(编译时的解决方案),那么一个足够先进的IDE将能够在您点击编译之前向您提供您编写的代码是坏的信息。
像Visual Studio这样的编译器本质上是"编译"的。在后台为你准备的东西,然后用红色的弯弯曲曲的线来强调不好的代码(读作:不会编译)。
编译时检查下面的答案
您为您的Class2
结构提出了几种可能性,因此让我们逐一解决:
首先我们将从你的Class1
开始基本上按照你的定义:
template<int i>
struct Class1{
// how to define a "foo" function that is only callable
// if Class2's j is evenly divisble by i?
};
第一种可能是Class2
有一个模板化的j
参数:
template<int j>
struct Class2
{
//...
};
一个好的解决方案是模板Class1
的foo
方法,然后包含一个static_assert
,这是一个编译时断言。这是因为i
和j
在编译时是已知的。
现在Class1
看起来像这样:
template<int i>
struct Class1{
template<int j>
void foo()
{
static_assert(j%i==0, "j is not evenly divisible by i");
std::cout << "j is evenly divisble by i" << std::endl;
}
};
Class2
可以这样调用foo
:
template<int j>
struct Class2
{
void CallFoo()
{
Class1<2> c1;
c1.foo<j>(); // works
//Class1<3> c2;
//c2.foo<2>(); // fails static assert
}
};
演示您提到的另一种可能性是Class2
可能有j
的成员变量。只要该成员变量是constexpr
(结果也是static
),就可以实现这一点:
struct Class2
{
static constexpr int j = 4;
void CallFoo()
{
Class1<2> c1;
c1.foo<j>(); // works
//Class1<3> c2;
//c2.foo<2>(); // fails static assert
}
};
constexpr
在这里定义了一个编译时常数。所以这个值在编译时是已知的,我们的static_assert
可以正常工作。
如果j
不是 constexpr
,那么我们无法实现编译时断言。此时,您被降级到运行时异常处理:
template<int i>
struct Class1{
void foo(int j)
{
if (j%i != 0)
throw std::invalid_argument("j is not evenly divisible by i");
std::cout << "j is evenly divisble by i" << std::endl;
}
};
struct Class2
{
int j = 4;
void CallFoo()
{
Class1<2> c1;
c1.foo(j); // works
j = 3;
c1.foo(j); // throws
}
};
演示- C++ 在编译时具有函数计算全局变量
- 在缺少函数重载时抛出异常,并带有 std::variant 而不是编译时错误
- 在编译时评估函数开销的通用方法
- 如何在常量计算表达式中获取编译时错误?
- 有没有办法在C++编译时更改函数原型?
- C++根据调用的构造函数强制编译时错误
- 使用 std::iterator_traits<> 时编译时错误不明确
- 尝试调用指向成员函数的函数指针时出现编译时错误
- 编译时错误:删除了联合默认构造函数
- 函数指针 - 编译时错误
- 函数编译时错误
- C++中与主函数对应的编译时错误
- C++:使用类型名作为基的模板类调用函数编译时错误
- 2个c源文件之间的函数指针操作导致编译时错误
- 如果条件不满足,调用函数时会出现编译时错误
- 作为私有的析构函数抛出编译时错误
- map非const函数,在clang++下编译时错误,适用于g++
- 调用专用模板函数时强制编译时错误
- 编译时错误:期望的构造函数、析构函数或转换
- 在工厂中使用指针函数会产生编译时错误