0

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

#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;
}

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

$ 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++ 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_typeT在这种情况下)。

所以,问题是:如何创建一个能够同时推断容器类型和值类型的通用函数?

我确定我遗漏了一些明显的东西 - 感谢您的耐心和帮助。

编辑(答案)

以下代码按预期运行:

#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
4

3 回答 3

5

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

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作为第一个函数参数的实例。因此,我的建议是放弃推断第一个参数是标准容器的实例(标准容器不是那么统一):

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

那么你可能想要做的是表达编译时约束,也许通过一个static_assert和基于一个自定义类型特征,它C必须是标准容器的一个实例。

或者,您可以使用KerrekSB 在他的回答中建议的可变参数模板(但这仍然不会阻止您传入任何其他类型模板的实例,甚至是非容器模板)。

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

progression(r, 10, 0.8, 10);

这里,第二个参数的类型是int,而容器元素的类型是double。这会在执行类型推导时混淆编译器。要么这样称呼它:

progression(r, 10.0, 0.8, 10);

或者让您的编译器为第二个参数推断出不同的类型(可能 SFINAE 将其限制为可以转换为元素类型的东西)。

于 2013-05-31T19:30:48.353 回答
4

容器通常有很多模板参数。幸运的是,可变参数模板中有一个特殊的子句,它允许对任何具体数量的参数使用包:

template <template <typename...> class Container, typename ...Args>
void foo(Container<Args...> const & c)
{
    // ...
}

(即使Container是非可变模板,特殊子句也会起作用。)

于 2013-05-31T19:30:21.023 回答
1

这失败的原因是因为你说中间的两个 args 是相同的类型,而实际上它们不是。例如,中间是浮点数,而另一个是整数。基本上你说 a 和 ratio 是相同的类型,但在通话中它们是不同的类型

于 2013-05-31T19:29:06.460 回答