简体   繁体   English

具有可选参数或重载的功能模板

[英]Function Template with optional argument or overloaded

I got a class, containing 20 structure elements in a classical C-Array. 我得到了一个类,其中包含20个经典C数组中的结构元素。 The elements form 0 to 5 belong to Type A, from 6 to 15 they belong to Type B and the rest belongs to Type C. For looping this elements, I designed three function templates. 形式从0到5的元素属于Type A,从6到15的元素属于Type B,其余元素属于TypeC。为循环此元素,我设计了三个功能模板。 Here is a very simple example of my problem (I know, it makes no sense, butit only demonstrates what I want): 这是我的问题的一个非常简单的示例(我知道,这没有任何意义,但仅说明了我想要的):

#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();
}

Well, that runs well. 好吧,那很好。 But sometimes, I also need the array-index of the element. 但有时,我还需要元素的数组索引。 As I still have lots of this function templates in my real programm, I try to avoid defining new functions but want to overload them or use an optional argument. 由于我的实际程序中仍然有很多此功能模板,因此我尝试避免定义新功能,而是想重载它们或使用可选参数。

I tried this was, but it doesn't run (just show the difference): 我试过了,但是没有运行(只是显示出区别):

    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();
}

Does anybody has an idea, how to solve that problem without defining complette new function members? 是否有人有想法,如何在不定义complette新函数成员的情况下解决该问题?

Thank you in advance, Jan 提前谢谢你,扬

If you can use constexpr then in constexpr if the code is optimized at compile time that means if false is passed compiler will directly compile else not if that means it wont give error. 如果可以使用constexpr,则在constexpr中,如果代码在编译时进行了优化,这意味着如果传递了false,则编译器将直接进行编译,否则,它将不会给出错误。

 if constexpr (GETINDEX){
    //do something f(par1,par2);
 }
 else{
   //or f(par1);
 }

now when u will compile this and GETINDEX is false f(par1,par2) wont get checked and else will be compiled. 现在,当您将其编译并且GETINDEX为false时,将不检查f(par1,par2),否则将对其进行编译。 This will help you to call function. 这将帮助您调用函数。

Running your code got me this error: 运行您的代码使我出现此错误:

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]);
             ~^~~~~~~~~~~~~~~~~~

So I suspected it had to do with your else case: 因此,我怀疑这与您的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
        }
    }
}

The important bit of information the error output provides is: 错误输出提供的重要信息是:

FUNCTION = main()::<lambda(MyStruct*, int)>

Since (I'm guessing) when the function template was evaluated, the template should work on all calls and instances, regardless whether or not they'd be executed. 由于(我猜)在评估功能模板时,该模板应适用于所有调用和实例,无论是否要执行它们。 Providing a default argument to your lambda fixed it: 为您的lambda提供默认参数可以解决该问题:

[&](MyStruct* pStr, int i = -1) {...}

or to your function call: 或您的函数调用:

else {
    for (int i = 0; i < aRange; i++) {
        f(&this->valArr[i], -1);       //  -1
    }
}

Your code runs fine afterwards. 之后,您的代码可以正常运行。

But using -1 may be a bit too hacky ("magic numbers" and all that), so I might opt for std::optional instead. 但是使用-1可能有点太过分了(“魔术数字”等等),所以我可能选择std::optional代替。

Your problem is, that the compiler needs to know which branch of the conditional to use at compile-time, because the functions have different signatures. 您的问题是,编译器需要知道在编译时使用条件语句的哪个分支,因为函数具有不同的签名。 Thus you have either the solution given by @TrebuchetMS, ie accept only functions with index. 因此,您可以使用@TrebuchetMS给出的解决方案,即仅接受带有索引的函数。 Or you have to somehow express your intents in the type system. 或者,您必须以某种方式表达您对类型系统的意图。

I see three possible solutions but there are probably more: 我看到了三种可能的解决方案,但可能还有更多解决方案:

1) Overload LoopRangeA for both types of functions like this: 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);
    }
}

This will select the kind of loop depending on the function signature. 这将根据功能签名选择循环的类型。 The drawback is, that you'll probably need to provide overloads for onst Mystruct* too. 缺点是,您可能还需要为onst Mystruct*提供重载。

2) Given you can use C++17, you could leverage if constexpr by providing a bool template parameter: 2)如果您可以使用C ++ 17,则可以通过提供bool模板参数来利用if constexpr

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]);
        }
    }
}

But, as @StoryTeller mentions in his / her comment, you have to pass redundant information, as the need for an index is encoded in the function signature anyways. 但是,正如@StoryTeller在他/她的评论中提到的那样,您必须传递冗余信息,因为无论如何都需要在函数签名中编码索引。

3) Therefore, I'd prefer the 3rd solution which takes the best of both others: 3)因此,我更喜欢第三个解决方案,该解决方案兼具其他优点:

First you provide a function that determines the ability to eat an index at compile time. 首先,您提供一个确定在编译时使用索引的功能的函数。 this needs some little 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 {};
}

Then you implement your function like this: 然后,您可以像下面这样实现您的功能:

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]);
        }
    }
}

Finally, this is the main function showing how to use the solutions: 最后,这是显示如何使用解决方案的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();
}

See here for a full example. 有关完整示例,请参见此处

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM