简体   繁体   English

为什么我的可变参数模板实例化不起作用?

[英]Why does my variadic template instantiation not work?

I am revisiting C++ after a long hiatus, and I would like to use templates to design the known "map" function -- the one which applies a specified function to every element of some specified "iterable" object. I am revisiting C++ after a long hiatus, and I would like to use templates to design the known "map" function -- the one which applies a specified function to every element of some specified "iterable" object.

Disregarding the fact my map doesn't return anything (a non-factor here), I have managed to implement what I wanted if the function passed to "map" does not need to accept additional arguments:忽略我的map没有返回任何东西(这里是非因素)的事实,如果 function 传递给“map”不需要接受额外的 ZDBC11CAA5BDA9E77E6FB4DABD8,我已经设法实现了我想要的:

#include <iostream>

template <typename C, void fn(const typename C::value_type &)> void map(const C & c) {
    for(auto i : c) {
        fn(i);
    }
}

struct some_container_type { /// Just some hastily put together iterable structure type
    typedef int value_type;
    value_type * a;
    int n;
    some_container_type(value_type * a, int n): a(a), n(n) { }
    value_type * begin() const {
        return a;
    }
    value_type * end() const {
        return a + n; 
    }
};

void some_fn(const int & e) { /// A function used for testing the "map" function
    std::cout << "`fn` called for " << e << std::endl;
}

int main() {
    int a[] = { 5, 7, 12 };
    const some_container_type sc(a, std::size(a));
    map<some_container_type, some_fn>(sc);
}

However, I would like map to accept additional arguments to call fn with.但是,我希望map接受额外的 arguments 来调用fn I've tried to compile the modified variant of the program (container type definition was unchanged):我试图编译程序的修改变体(容器类型定义未更改):

template <typename C, typename ... T, void fn(const typename C::value_type &, T ...)> void map(const C & c, T ... args) {
    for(auto i : c) {
        fn(i, args...);
    }
}

void some_fn(const int & e, int a, float b, char c) {
    std::cout << "`fn` called for " << e << std::endl;
}

int main() {
    int a[] = { 5, 7, 12 };
    const some_container_type sc(a, std::size(a));
    map<some_container_type, int, float, char, some_fn>(sc, 1, 2.0f, '3');
}

But gcc -std=c++20 refuses to compile the modified program containing the above variant, aborting with:但是gcc -std=c++20拒绝编译包含上述变体的修改程序,中止:

<source>: In function 'int main()':
<source>:29:56: error: no matching function for call to 'map<some_container_type, int, float, char, some_fn>(const some_container_type&, int, int, int)'
   29 |     map<some_container_type, int, float, char, some_fn>(sc, 1, 2, 3);
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
<source>:16:97: note: candidate: 'template<class C, class ... T, void (* fn)(const typename C::value_type&, T ...)> void map(const C&, T ...)'
   16 | template <typename C, typename ... T, void fn(const typename C::value_type &, T ... args)> void map(const C & c, T ... args) {
      |                                                                                                 ^~~
<source>:16:97: note:   template argument deduction/substitution failed:
<source>:29:56: error: type/value mismatch at argument 2 in template parameter list for 'template<class C, class ... T, void (* fn)(const typename C::value_type&, T ...)> void map(const C&, T ...)'
   29 |     map<some_container_type, int, float, char, some_fn>(sc, 1, 2, 3);
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
<source>:29:56: note:   expected a type, got 'some_fn'

Microsoft Visual C++ compiler ( 19.24.28314 ) gives a more descriptive error message: Microsoft Visual C++ 编译器 ( 19.24.28314 ) 提供更具描述性的错误消息:

error C3547: template parameter 'fn' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'map'

Can someone explain if and how I can idiomatically accomplish for map to accept arbitrary arguments for forwarding these to fn ?有人可以解释我是否以及如何以惯用方式完成map接受任意 arguments 将这些转发到fn

I know I can pass fn to the map function as argument instead of specifying it as an argument to the template , but for reasons related to inlining and understanding limitations of C++ templates, I'd like to retain fn a template rather than a function parameter. I know I can pass fn to the map function as argument instead of specifying it as an argument to the template , but for reasons related to inlining and understanding limitations of C++ templates, I'd like to retain fn a template rather than a function parameter .

I also don't want to use any libraries, including the standard library (what use of std I show in the examples above is only for clarifying the question).我也不想使用任何库,包括标准库(我在上面的示例中显示的std的用途只是为了澄清问题)。 I know there are "functor" and "forward" somewhere in the libraries, but I suppose they too were written in C++, so I am curious if my problem can be solved without any libraries.我知道库中的某处有“函子”和“转发”,但我想它们也是用 C++ 编写的,所以我很好奇我的问题是否可以在没有任何库的情况下解决。

A simple way to fix this would be to deduce the non-type template parameter for the function, and reorder the template parameter list解决此问题的一种简单方法是推导出 function 的非类型模板参数,并重新排序模板参数列表

template <typename C, auto fn, typename ... T> 
void map(const C & c, T ... args) {
    for(auto i : c) {
        fn(i, args...);
    }
}

and then call it like this然后这样称呼它

map<some_container_type, some_fn, int, float, char>(sc, 1, 2.0f, '3');

Here's a demo这是一个演示


You could also move fn to the beginning of the template parameter list.您还可以将fn移动到模板参数列表的开头。

template <auto fn, typename C, typename ... T> 
void map(const C & c, T ... args) {
    for(auto i : c) {
        fn(i, args...);
    }
}

Now since C and T can be deduced from the function arguments, this makes the call site much cleaner现在由于CT可以从 function arguments 推导出来,这使得呼叫站点更加清晰

map<some_fn>(sc, 1, 2.0f, '3');

Here's a demo这是一个演示

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

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