【问题标题】:Allow template parameter of function pointer type to accept functions of any return type允许函数指针类型的模板参数接受任何返回类型的函数
【发布时间】:2021-01-27 08:25:32
【问题描述】:

当函数的返回值没有被实际使用时,有没有办法允许函数指针类型的模板参数接受任何(而不是特定的)返回类型的函数?这是一个 MCVE 来说明我的意思:

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

我知道我可以这样做,作为一种解决方法:

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
}

甚至这个(在我看来更糟,因为我们失去了对函数类型的参数类型的类型检查并且可能落入 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
}

但我想知道是否有一种方法可以在不添加没有其他用途的额外模板参数的情况下做到这一点。

【问题讨论】:

  • 您使用的是哪个 C++ 版本?
  • @florestan Unspecified - 如果它可能符合某些特定标准(最高和/或包括 c++20),那将是一个很好的答案。 (目前我的目标是 c++17,但不要将其视为对答案的限制。)
  • C++17 为非类型模板参数提供auto
  • 由于客户端 API 不是函数模板(或者相反,类模板),因此很多时候答案可能是专业化,因为部分类模板专业化提供了详细的实现(/演绎)控制,同时不会弄乱客户端(模板参数)API。我添加了一个答案来展示这种方法。

标签: c++ templates


【解决方案1】:

您可以将模板参数声明为auto(C++17 起),使其适用于任何函数指针类型。

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

LIVE

如果你想用 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

或者仍然将模板参数声明为可能返回任何类型的函数指针。

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

LIVE

【讨论】:

【解决方案2】:

我知道我可以这样做,作为一种解决方法:

[...]

但我想知道是否有一种方法可以在不添加没有其他用途的额外模板参数的情况下做到这一点。

虽然 OP 的解决方法提到了额外的模板参数,但它们被用作主模板的参数,妨碍客户端在使用调度 API 时在模板参数列表中显式指定它们。

这里的关键,w.r.t。使用额外的模板参数,是(如my detailed answer to the related follow-up question所示)专业化

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

部分特化中的附加模板参数对客户端不可见,完全可以从与客户端 API 关联的非类型模板参数推导出来。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-29
    • 1970-01-01
    • 2014-01-04
    相关资源
    最近更新 更多