Pau*_*han 1 c++ templates c++11
此代码不会自动正确推断返回类型(C++的设计方面):
template < typename Container,
typename UnaryOp>
Container
mymap(Container c, UnaryOp op)
{
typedef typename Container::value_type ResultType
Container<ResultType> result;
for(Container::iterator i = c.begin();
i != c.end();
i++)
{
result.push_back(op(*i));
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
我想做的是发生这样的事情:
vector<string> bar;
bar.push_back("1");
bar.push_back("2");
bar.push_back("3");
vector<int> foomatic;
foomatic = mymap(bar, [] (string s)->int {return atoi(s.c_str());});
//foomatic now is equal to {1,2,3}
Run Code Online (Sandbox Code Playgroud)
我认为这Container将被推断为vector,并且ResultType将被推断为int.
Container对于输入和输出,您使用相同的类型.但是您的输入和输出类型是不同的:您的输入是vector<string>,而您的输出是vector<int>.难怪C++拒绝编译它.
您现在的问题是从输入类型中推断出返回类型.通常,C++ 不能这样做.它就是这么简单:重载决策和模板解析只发生在输入参数上,永远不会在返回类型上发生(在某些情况下,涉及代理对象的精巧技巧和隐式强制转换可以用来解决这个问题,但不要去那里).
最简单和最惯用的解决方案是在调用函数时手动指定返回元素类型,如:
foomatic = mymap<int>(bar, [] (string s)->int {return atoi(s.c_str());});
Run Code Online (Sandbox Code Playgroud)
这要求将返回元素类型放在模板参数列表的第一位:
template <
typename ResultType,
template<typename> class Container,
typename InputType,
typename UnaryOp>
Container<ResultType> mymap(Container<InputType> c, UnaryOp op) { ... }
Run Code Online (Sandbox Code Playgroud)
但是,这不起作用,因为std::vector不符合声明template<typename> class.为什么?原因很简单:因为它不仅仅有一个模板参数.特别是,标准说它至少有一个额外的模板参数来指定分配器.
解决方案:将模板参数声明为template<typename, typename> class,对吗?
现在号,这确实对工作的一些标准库的实现.但除了强制的两个模板参数之外,容器可能还有其他模板参数,这些参数采用默认值(例如,这通常用于将策略类传递给容器;分配器已经是这样的策略类).
这是一个基本问题:我们无法声明Container它符合C++中容器的所有可能类型签名.所以这个解决方案也是不行的.
不幸的是,最好的解决方案更复杂,我们需要明确重新绑定容器类型.我们可以通过一个额外的元函数来做:
template <typename C, typename T>
struct rebind;
Run Code Online (Sandbox Code Playgroud)
我们需要为每个可能数量的模板参数部分地专门化这个元函数.例如,为了使它与minimal一起工作std::vector,我们需要以下部分特化:
template <
template <typename, typename> class C,
typename Old,
typename New,
typename A>
struct rebind<C<Old, A>, New> {
typedef typename A::template rebind<New> Rebound;
typedef C<New, typename Rebound::other> type;
};
Run Code Online (Sandbox Code Playgroud)
这看起来令人生畏.它的作用是将一个现有的std::vector<foo>和一个类型bar重写为一个std::vector<bar>.棘手的部分是我们还需要重写分配器类型.这是通过相当复杂的Rebound声明来完成的.
现在我们可以编写你的函数,然后调用它:
template <
typename ResultType,
typename C,
typename UnaryOp>
typename rebind<C, ResultType>::type
mymap(C const& c, UnaryOp op)
{
typename rebind<C, ResultType>::type result;
for(typename C::const_iterator i = c.begin();
i != c.end();
i++)
{
result.push_back(op(*i));
}
return result;
}
int main() {
vector<string> bar;
bar.push_back("1");
bar.push_back("2");
bar.push_back("3");
vector<int> foomatic =
mymap<int>(bar, [] (string s)->int {return atoi(s.c_str());});
}
Run Code Online (Sandbox Code Playgroud)
小菜一碟.一个非常非常复杂的蛋糕.
如果您有一个模板参数本身就是一个类模板,您需要将其声明为:
template <
template<typename> class Container,
typename ResultType,
typename UnaryOp>
Container<ResultType> mymap(Container<ResultType> c, UnaryOp op) { ... }
Run Code Online (Sandbox Code Playgroud)
该template<typename> class Container模拟类模板声明语法和告诉编译器" Container是只需要一个模板参数类模板."
但是库通常会避免使用这些嵌套模板声明,而是依赖traits/metafunctions来传递这些信息.也就是说,它通常写成如下:
template <typename Container, typename UnaryOp>
Container mymap(Container c, UnaryOp op) {
typedef typename Container::value_type ResultType;
}
Run Code Online (Sandbox Code Playgroud)
(typenametypedef中的名称是必需的,因为名称是依赖名称,C++无法确定它是否为类型命名.)
此示例模仿标准库约定,即value_type在每个容器中为其关联的值类型设置typedef .其他库可能遵循不同的模式.例如,我正在为使用外部元函数的库做出贡献,其工作方式如下:
template <typename Container, typename UnaryOp>
Container mymap(Container c, UnaryOp op) {
typedef typename Value<Container>::Type ResultType;
}
Run Code Online (Sandbox Code Playgroud)
这个想法是一样的,唯一的区别是Container::value_type已经"外包"到一个独立的类型.
| 归档时间: |
|
| 查看次数: |
990 次 |
| 最近记录: |