Allow template parameter of function pointer type to accept functions of any return type

对着背影说爱祢 提交于 2021-02-10 11:45:09

问题


Is there a way to allow a template parameter of function pointer type to accept a function of any (rather than a specific) return type, when the function's return value is not actually used? Here's an MCVE to illustrate what I mean:

int returnInt(int) { return 0; }
void returnVoid(int) { }

template <int (*Func)(int)>
struct foo { void bar(int x) { Func(x); } };

int main(int, char *[]) {
    foo<returnInt> a; // ok
    foo<returnVoid> b; // argument of type "void (*)(int)" is incompatible
                       // with template parameter of type "int (*)(int)"C/C++(458)
}

I know I can do this, as a workaround:

template <typename ReturnType, ReturnType (*Func)(int)>
struct foo { void bar(int x) { Func(x); } };

int main(int, char *[]) {
    foo<int, returnInt> a; // ok
    foo<void, returnVoid> b; // ok
}

Or even this (which is worse in my opinion, because we lose the type check on the function type's argument types and could fall into SFINAE):

template <typename FuncType, FuncType *Func>
struct foo { void bar(int x) { Func(x); } };

int main(int, char *[]) {
    foo<decltype(returnInt), returnInt> a; // ok
    foo<decltype(returnVoid), returnVoid> b; // ok
}

But I wonder if there's a way to do it without adding an extra template parameter which has no other purpose.


回答1:


You can declare the template parameter as auto (since C++17) to make it working with any function pointer types.

template <auto Func>
struct foo { void bar(int x) { Func(x); } };

LIVE

And if you want to check the type with SFINAE:

template <auto Func, std::enable_if_t<std::is_function_v<std::remove_pointer_t<decltype(Func)>>> * = nullptr>
struct foo { void bar(int x) { Func(x); } };

LIVE

Or still declare the template parameter as function pointer which might return any types.

template <auto (*Func)(int)>
struct foo { void bar(int x) { Func(x); } };

LIVE




回答2:


I know I can do this, as a workaround:

[...]

But I wonder if there's a way to do it without adding an extra template parameter which has no other purpose.

Although the workaround by OP mentions additional template parameters, they are used as parameters to the primary template, encumbering the client to explicitly specify them in the template argument list when making use of the dispatch API.

The key here, w.r.t. to using additional template parameters, is (as shown in my detailed answer to the related follow-up question) specialization:

template <auto>
struct foo { 
    void bar(int x) = delete; 
};

template <typename T, typename Return, Return (*Func)(T)>
struct foo<Func> {
    void bar(int x) {
        Func(x);
    }
};

int returnInt(int) { return 0; }
void returnVoid(int) { }

int main() {
    foo<returnInt> a;  // OK (GCC and Clang)
    foo<returnVoid> b; // OK (GCC and Clang)
}

The additional template parameters in the partial specialization are not visible to the client, and are entirely deducible from the non-type template parameter associated with the client API.



来源:https://stackoverflow.com/questions/65915344/allow-template-parameter-of-function-pointer-type-to-accept-functions-of-any-ret

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!