我有一个类,在一个经典的C数组中包含20个结构元素。形式从0到5的元素属于Type A,从6到15的元素属于Type B,其余元素属于TypeC。 为了循环这些元素,我设计了三个功能模板。这是我的问题的一个非常简单的示例(我知道,这没有任何意义,但仅演示了我想要的):
#include <iostream>
#include <string>
struct MyStruct {
int Value;
MyStruct() {
this->Value = 0;
}
MyStruct(int fValue) {
this->Value = fValue;
}
void PrintValue() { std::cout << "Value: " << std::to_string(this->Value) << std::endl; }
};
class MyClass {
private:
struct MyStruct valArr[20];
int aRange = 5;
int bRange = 10;
public:
MyClass() {
for (int i = 0; i < 20; i++) {
valArr[i] = MyStruct(i);
}
}
template<typename FUNCTION>
inline void LoopRangeA(FUNCTION f, bool GetIndex = false) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i]);
}
}
template<typename FUNCTION>
inline void LoopRangeB(FUNCTION f) {
for (int i = aRange; i < bRange; i++) {
f(&this->valArr[i]);
}
}
template<typename FUNCTION>
inline void LoopRangeC(FUNCTION f) {
for (int i = bRange; i < 20; i++) {
f(&this->valArr[i]);
}
}
template<typename FUNCTION>
inline void LoopAll(FUNCTION f) {
for (int i = 0; i < 20; i++) {
f(&this->valArr[i]);
}
}
};
int main() {
MyClass Cls = MyClass();
Cls.LoopRangeA([](MyStruct* pStr) {pStr->PrintValue(); });
std::cout << "Application is finished. Press ENTER to exit..." << std::endl;
std::cin.get();
}
嗯,运行良好。但有时,我还需要元素的数组索引。由于我的实际程序中仍然有很多这样的功能模板,因此我尽量避免定义新功能,而是想重载它们或使用可选参数。
我尝试过,但是没有运行(只是显示出区别):
template<typename FUNCTION>
inline void LoopRangeA(FUNCTION f, bool GetIndex = false) {
if (GetIndex) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i], i);
}
}else {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i]);
}
}
}
int main() {
MyClass Cls = MyClass();
Cls.LoopRangeA([](MyStruct* pStr, int& i) {std::cout << "Index: " << std::to_string(i); pStr->PrintValue(); std::cout << "" << std::endl; }, true);
std::cout << "Application is finished. Press ENTER to exit..." << std::endl;
std::cin.get();
}
有人有一个主意,如何在不定义complette新函数成员的情况下解决该问题?
预先感谢您, 扬
答案 0 :(得分:0)
如果可以使用constexpr,则在constexpr中,如果代码在编译时进行了优化,这意味着如果传递了false,则编译器将直接进行编译,否则,它将不会给出错误。
if constexpr (GETINDEX){
//do something f(par1,par2);
}
else{
//or f(par1);
}
现在您何时将编译它,而GETINDEX为false f(par1,par2)将不被检查,否则将被编译。这将帮助您调用函数。
答案 1 :(得分:0)
运行您的代码使我收到此错误:
In instantiation of 'void MyClass::LoopRangeA(FUNCTION, bool) [with FUNCTION = main()::<lambda(MyStruct*, int)>]':
46:14: error: no match for call to '(main()::<lambda(MyStruct*, int)>) (MyStruct*)'
f(&this->valArr[i]);
~^~~~~~~~~~~~~~~~~~
所以我怀疑这与您的else
案有关:
template<typename FUNCTION>
inline void LoopRangeA(FUNCTION f, bool GetIndex = false) {
if (GetIndex) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i], i);
}
}else {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i]); // <-- this guy over here
}
}
}
错误输出提供的重要信息是:
FUNCTION = main()::<lambda(MyStruct*, int)>
自从(我猜)函数模板被评估以来,该模板应可用于所有调用和实例,无论它们是否将被执行。为您的lambda提供默认参数可以解决该问题:
[&](MyStruct* pStr, int i = -1) {...}
或转到您的函数调用:
else {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i], -1); // -1
}
}
您的代码此后运行正常。
但是使用-1
可能有点太过分了(“魔术数字”等等),所以我可能会选择std::optional
。
答案 2 :(得分:0)
您的问题是,编译器需要知道在编译时使用条件语句的哪个分支,因为函数具有不同的签名。因此,您可以使用@TrebuchetMS提供的解决方案,即仅接受带有索引的函数。或者,您必须以某种方式表达您对类型系统的意图。
我看到了三种可能的解决方案,但可能还有更多解决方案:
1)像这样的两种类型的函数都重载LoopRangeA
:
inline void LoopRangeA(void (*f)(MyStruct*)) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i]);
}
}
inline void LoopRangeA(void (*f)(MyStruct*, size_t)) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i], i);
}
}
这将根据功能签名选择循环的类型。缺点是,您可能还需要为onst Mystruct*
提供重载。
2)如果您可以使用C ++ 17,则可以通过提供if constexpr
模板参数来利用bool
:
template<bool GetIndex, typename FUNCTION>
void LoopRangeA1(FUNCTION f) {
if constexpr(GetIndex) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i], i);
}
} else {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i]);
}
}
}
但是,就像@StoryTeller在他/她的评论中提到的那样,您必须传递冗余信息,因为无论如何都需要在函数签名中编码索引。
3)因此,我更喜欢第三个解决方案,该解决方案兼具其他优点:
首先,您提供一个确定在编译时使用索引的功能的函数。这需要一些constexpr-trickery:
constexpr std::false_type eats_index(...) { return {}; }
template<typename T>
constexpr auto eats_index(T) -> decltype(std::declval<T>()(std::declval<MyStruct*>(), 0), std::true_type{}) {
return {};
}
然后您就可以像这样实现您的功能:
template<typename FUNCTION>
void LoopRangeA2(FUNCTION f) {
if constexpr(eats_index(f)) {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i], i);
}
} else {
for (int i = 0; i < aRange; i++) {
f(&this->valArr[i]);
}
}
}
最后,这是main
函数,显示如何使用解决方案:
int main() {
MyClass Cls = MyClass();
auto print_no_idx = [](MyStruct* pStr) {pStr->PrintValue(); };
auto print_const_no_idx = [](const MyStruct* pStr) { };
auto print_with_idx = [](MyStruct* pStr, size_t idx) {
std::cout << "index: " << idx << " -> ";
pStr->PrintValue(); };
Cls.LoopRangeA(print_no_idx);
Cls.LoopRangeA(print_const_no_idx); // <- does not compile, you'd need another overload
Cls.LoopRangeA(print_with_idx);
Cls.LoopRangeA1<false>(print_no_idx);
Cls.LoopRangeA1<false>(print_const_no_idx); // <- works w/o additional overload
Cls.LoopRangeA1<true>(print_with_idx);
static_assert(!eats_index(print_no_idx));
static_assert(eats_index(print_with_idx));
Cls.LoopRangeA2(print_no_idx);
Cls.LoopRangeA2(print_const_no_idx); // <- works, w/o additional overload
Cls.LoopRangeA2(print_with_idx);
std::cout << "Application is finished. Press ENTER to exit..." << std::endl;
std::cin.get();
}
有关完整示例,请参见here。