使用模板模板参数时失败的模板参数推导 [英] Failed template argument deduction when using template template parameters

查看:474
本文介绍了使用模板模板参数时失败的模板参数推导的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个简单的辅助算法,以几何级数填充容器(例如std::vector<T>)(第一个项是a,第n个项由a * pow(r, n-1)给出) ,其中r是给定的比率);我创建了以下代码:

I wanted to create a simple auxiliary algorithm that would fill a container, such as std::vector<T>, with a geometric progression (the first term is a, and the n-th term is given by a * pow(r, n-1), where r is a given ratio); I created the following code:

#include<vector>
#include<algorithm>
#include<iostream>

template<template <typename> class Container, typename T>
void progression(Container<T>& container, T a, T ratio, size_t N) {
  if(N > 0) {    
    T factor = T(1);  
    for(size_t k=0; k<N; k++) {
      container.push_back(a * factor);
      factor *= ratio;
    }
  }
}

int main() {
  std::vector<double> r;
  progression(r, 10.0, 0.8, static_cast<size_t>(10));

  for(auto item : r) {
    std::cout<<item<<std::endl;
  }

  return 0;
}

在尝试编译时会产生以下错误:

which yields the following errors upon attempting compilation:

$ g++ geometric.cpp -std=c++11 # GCC 4.7.2 on OS X 10.7.4
geometric.cpp: In function ‘int main()’:
geometric.cpp:18:52: error: no matching function for call to ‘progression(std::vector<double>&, double, double, size_t)’
geometric.cpp:18:52: note: candidate is:
geometric.cpp:6:6: note: template<template<class> class Container, class T> void progression(Container<T>&, T, T, size_t)
geometric.cpp:6:6: note:   template argument deduction/substitution failed:
geometric.cpp:18:52: error: wrong number of template arguments (2, should be 1)
geometric.cpp:5:36: error: provided for ‘template<class> class Container’

Clang的错误消息更加微妙:

Clang's error message is more subtle:

$ clang++ geometric.cpp -std=c++11 # clang 3.2 on OS X 10.7.4 
geometric.cpp:18:3: error: no matching function for call to 'progression'
  progression(r, 10, 0.8, 10);
  ^~~~~~~~~~~
geometric.cpp:6:6: note: candidate template ignored: failed template argument deduction
void progression(Container<T>& container, T a, T ratio, size_t N) {
     ^
1 error generated.

我希望使用模板模板参数不仅可以推断出容器,而且可以推断出容器的value_type(在这种情况下为T).

I would have expected that using template template parameters I would be able to deduce not only the container, but also the container's value_type (T in this case).

所以,问题是:我如何创建可以推断容器类型和值类型的泛型函数?

我确信我缺少明显的东西-感谢您的耐心配合和帮助.

I am sure I'm missing something obvious - I appreciate your patience and help.

以下代码的行为符合预期:

The following code behaves as expected:

#include<vector>
#include<algorithm>
#include<iostream>

template<template <typename...> class Container, typename T, typename... Args>
void progression(Container<Args...>& container, T a, T ratio, size_t N) {
  if(N > 0) {    
    T factor = T(1);  
    for(size_t k=0; k<N; k++) {
      container.push_back(a * factor);
      factor *= ratio;
    }
  }
}

int main() {
  std::vector<double> r;
  progression(r, 10.0, 0.8, 10);

  for(auto item : r) {
    std::cout<<item<<std::endl;
  }

  return 0;
}

输出:

10
8
6.4
5.12
4.096
3.2768
2.62144
2.09715
1.67772
1.34218

推荐答案

第一个问题是,您忘记了std::vector<>是一个接受 two 的类模板.模板参数(元素类型和分配器),而不是一个.当您使用模板模板参数时,第二个模板参数具有默认值的事实是无关紧要的:

The first problem is, that you are forgetting that std::vector<> is a class template accepting two template parameters (the element type and the allocator), not one. The fact that the second template parameter has a default value is irrelevant when you are using template template parameters:

template<template <typename, typename> class Container, typename T, typename A>
//                           ^^^^^^^^                               ^^^^^^^^^^
void progression(Container<T, A>& container, T a, T ratio, size_t N) {
//                         ^^^^
// ...
}

注意,这将使得不可能将例如std::mapstd::unordered_map的实例作为第一个函数参数传递.因此,我的建议是放弃推论,第一个参数是标准容器的实例(标准容器不是那么统一):

Notice, that this will make it impossible to pass, for instance, an instance of std::map or std::unordered_map as the first function argument. Therefore, my suggestion is to give up deducing that the first argument is the instance of a standard container (standard containers are just not that uniform):

template<typename C, typename T, typename A>
//       ^^^^^^^^^
void progression(C& container, T a, T ratio, size_t N) {
//               ^^
// ...
}

然后您可能想做的是表达编译时的 constraint ,也许是通过static_assert并基于自定义类型特征,C必须是a的实例.标准容器.

What you may want to do then is to express the compile-time constrain, perhaps through a static_assert and based on a custom type trait, that C must be an instance of a standard container.

或者,您也可以按照KerrekSB在其答案中的建议使用通过传递任何其他类型的模板(甚至是非容器模板)的实例来实现.

Alternatively, you can use variadic templates as suggested by KerrekSB in his answer (but that still won't prevent you from passing in an instance of any other kind of template, even non-container ones).

第二个问题是您调用模板的方式:

The second problem is in the way you are calling your template:

progression(r, 10, 0.8, 10);

此处,第二个参数的类型为int,而容器元素的类型为double.在执行类型推导时,这会使编译器感到困惑.可以这样称呼它:

Here, the type of the second argument is int, while the type of the container element is double. This will confuse the compiler when performing type deduction. Either call it this way:

progression(r, 10.0, 0.8, 10);

或者允许您的编译器为第二个参数推断出不同的类型(可能是SFINAE约束它,使其可以转换为元素类型).

Or allow your compiler to deduce a different type for the second argument (possible SFINAE-constraining it to be something that can convert to the element type).

这篇关于使用模板模板参数时失败的模板参数推导的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆