【问题标题】:Failed template argument deduction when using template template parameters使用模板模板参数时模板参数推导失败
【发布时间】:2013-05-31 19:25:55
【问题描述】:

我想创建一个简单的辅助算法,以几何级数填充容器,例如std::vector<T>(第一项是an-th 由a * 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_type(在本例中为T)。

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

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

编辑(回答)

以下代码按预期运行:

#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

【问题讨论】:

    标签: c++ templates type-deduction


    【解决方案1】:

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

    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 必须是标准容器。

    或者,您可以使用可变参数模板 as suggested by KerrekSB in his answer(但这仍然不会阻止您传入任何其他类型模板的实例,甚至是非容器模板)。

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

    progression(r, 10, 0.8, 10);
    

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

    progression(r, 10.0, 0.8, 10);
    

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

    【讨论】:

    • 我实际上有这个答案的改进版本,但是我的网络连接中断,我无法从我的手机编辑(这只是一团糟)。
    【解决方案2】:

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

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

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

    【讨论】:

    • 这不是可变参数模板
    • 我从没想过这样做。这很整洁。
    • 谢谢 - 最终实现使用您建议的语法。
    【解决方案3】:

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

    【讨论】:

    • 我想你的意思是,中间是double,前面的也应该是。
    • 看看我的编辑 - 这不是问题。即使我通过两个doubles 并将整数转换为size_t,问题仍然存在。
    • @aaronman - 这实际上是一个问题 - 在我删除第一个错误后,您描述的错误确实出现了。
    • @Arrieta 很遗憾我无法回答第二个问题,你能解释一下模板的语法吗
    猜你喜欢
    • 1970-01-01
    • 2012-12-06
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-28
    • 2012-10-26
    • 1970-01-01
    相关资源
    最近更新 更多