3

我想使用一种类型创建一个模板,并传递一个具有包含该类型的模板参数的函数。

这是我目前拥有的:

#include <iostream>

void fooRef(int& ref) { std::cout << "In ref" << std::endl; }
void fooPtr(int* ptr) { std::cout << "In ptr" << std::endl; }

template<typename T, typename F>
void Print(T arg, F func) {
    //DoABunchOfStuff();
    func(arg);
    //DoSomeMoreStuff();
}

int main() {
    int x = 5;
    int& ref = x;

    Print<int*, void(*)(int*)>(&x, &fooPtr);
    Print<int&, void(*)(int&)>(ref, &fooRef);
}

这行得通,但我觉得函数的调用者可能会有一些额外的冗长。理想情况下,我希望调用看起来像:

Print<int*, fooPtr>(ptr);
Print<int&, fooRef>(ref); 

有没有办法简化对 Print 函数的调用?

4

3 回答 3

3

有没有办法简化对 Print 函数的调用?

是的。您所做的根本不是指定模板类型。函数模板经过一个过程调用模板参数推导。在这个过程中,传递给函数的参数的类型被推导出来,编译器尝试将其与模板参数匹配。如果它有效,则该函数被删除,编译器继续运行。所以如果我们使用了你的代码

int main() {
    int x = 5;
    int& ref = x;

    Print(&x, &fooPtr);
    Print(std::ref(ref), &fooRef);
}

然后我们得到

In ptr
In ref

Live Example

在我使用的第二个调用中std::ref,它实际上会ref通过引用传递。否则它将复制ref所指的任何内容。

于 2016-09-26T17:18:29.250 回答
1

您可以通过使用模板参数推导和可变参数模板来简化它。以下是如何做到这一点:

template<typename FunctionType, typename... ArgumentTypes>
void Print(FunctionType function, ArgumentTypes... args) {
    function(args...);
}

但是,如果您想正确支持引用,则必须求助于转发引用,这让编译器不仅可以推断发送的参数的类型,还可以推断它的值类型。

template<typename F, typename... Args>
void Print(F function, Args&&... args) {
    function(std::forward<Args>(args)...);
}

现在一切似乎都很好,但是您仍然必须处理接受任何参数的函数。可以使用尾随返回类型来限制您的函数只接受使调用有效的参数:

// instead of specifying the return type before the function name,
// let's place it at the end and use `decltype` to deduce the right type:
template<typename F, typename... Args>
auto Print(F function, Args&&... args) -> decltype(function(std::declval<Args>()...)) {
    return function(std::forward<Args>(args)...);
}

使用此代码,您的编译器会出现更清晰的错误:

void func1(int) {}

Print(func1, 1); // works!
Print(func1, 1, 7); // error: no such function to call

如果您仍然想要void作为您的返回类型并且仍然限制您的功能,您可以依靠void_t

// void_t definition
template<typename...>
using void_t = void;

template<typename F, typename... Args>
auto Print(F function, Args&&... args) -> void_t<decltype(function(std::declval<Args>()...))> {
    function(std::forward<Args>(args)...);
}
于 2016-09-26T17:30:50.127 回答
1

是的,您理想的语法很简单:

void fooRef(int& ref) { (void)ref; std::cout << "In ref" << std::endl; }
void fooPtr(int* ptr) { (void)ptr; std::cout << "In ptr" << std::endl; }

template<class T, void(*func)(T)>
void Print(T arg) {
  //DoABunchOfStuff();
  func(std::forward<Arg>(arg));
  //DoSomeMoreStuff();
}

int main() {
  int x = 5;
  int& ref = x;

  Print<int*, fooPtr>(&x);
  Print<int&, fooRef>(ref);
}

活生生的例子


(void)ref(void)ptr添加以抑制未使用的变量警告。 std::forward<T>用于使某些极端情况更有效。我还建议通过以下方式阻止扣除T

template<class T>struct tag_t{using type=T;};
template<class T>using block_deduction=typename tag_t<T>::type;

template<class T, void(*func)(T)>
void Print(block_deduction<T> arg) {

这将强制调用者T显式传入,只是因为我认为T这里的推导会使代码有点脆弱(它必须与 的签名完全匹配func)。

于 2016-09-26T18:23:07.747 回答