如何编写一个C++模板,可以接受任何类和类模板?

4

请注意:这个问题看起来比实际情况要更加显而易见。

我想写一个模板,它可以接受任何具体类或模板类作为模板参数。这可能看起来毫无意义,因为不知道传入的T是否被模板化,你将不知道如何使用它。我之所以想要这样做,是为了声明一个没有定义的通用模板,然后让用户对其进行专门化。因为用户正在对其进行专门化,他们总是知道处理的类型。但是,在未声明模板之前,用户无法对其进行专门化。

你可以这样做:

template<class T>
class myclass;

但是,如果您传递一个模板T,例如 myclass<std::vector>,这种方法将无法工作。因此我们尝试使用以下代码:

template<class T>
class myclass;

template<template<class> T>
class myclass;

这可能是正确的解决方案,但原来的代码无法运行,因为类模板不能重载。我们需要将其转换为函数模板,函数模板可以实现重载:

template<class T>
void myfunc();

template<template<class> T>
void myfunc();

太好了,我们完成了吗?但是,给模板模板参数传递的参数数量可能不同,因此我们也需要考虑这一点。

template<class T>
void myfunc();

template<template<class> T>
void myfunc();

template<template<class, class> T>
void myfunc();

template<template<class, class, class> T>
void myfunc();

// etc.

虽然不太美观,但是Boost Preprocessor库可以为我们生成此代码(在C++0x中将添加对可变参数模板的支持,因此这种丑陋只是暂时的)。但是我们仍然忘记了一种情况!如果T的某个参数不是类而是常量整数,该怎么办呢?让我们尝试支持它:

template<class T>
void myfunc();

template<template<class> T>
void myfunc();

template<template<class, class> T>
void myfunc();

template<template<class, class, class> T>
void myfunc();

// etc.

template<template<class> T>
void myfunc();

template<template<class, int> T>
void myfunc();

template<template<int, class> T>
void myfunc();

template<template<int, class, class> T>
void myfunc();

template<template<class, int, class> T>
void myfunc();

template<template<class, class, int> T>
void myfunc();

// etc.

哦哦。鉴于任何常量类型都可以传递到模板中,以任意数量混合类参数,会导致KABLOOEY组合爆炸。为了使事情更加困难,如果T的任何参数本身是模板呢?


6
我会重新开始并清晰地定义需求。你想要达到什么目的?混合模板和类是否有意义(请注意,std::vector<int>是一个类,不是模板)?你真正想解决什么问题? - David Rodríguez - dribeas
3个回答

3
boost::mpl类库做了类似于这样的事情(这是他们绑定参数的想法)。然而,要使其正常工作,您需要做出许多假设,例如使用;。
template <class T> foo { }; typedef foo< int_<4> > my_foo_4;

替代

template <int T> foo { }; typedef foo<4> my_foo_4;

不需要为所有int、char、bool等组合提供重载。

我认为没有比boost::mpl方法更有效的了,一般来说,任何方法都会遇到很多问题;类模板不是一种类型,不能真正地嵌入到类型系统中(boost::mpl将其视为创建新类型的函数;更通用地说,它用于创建“元函数”)。我甚至不确定可变参数模板是否会影响模板模板参数(虽然这是一个有趣的问题)。


1
托德,他们会的。在C++1x中,你能够做到模板<typename>结构Take;模板<template<typename...> class T>结构Take<T<stuff>>{}; 你也可以接受任何类型并从它们的参数中获取template<template<typename...> class T, typename... Ts>结构Take<T<Ts...>>{...}; - Johannes Schaub - litb
使用int_<4>是我曾经想过的一个主意,但我害怕走这条路 ;) 我必须为模板模板参数做同样的事情,并将参数插入其中,这已经超出了我能轻松想象的范围。我想我只需要尝试一下,看看会发生什么。 - Joseph Garvin
MPL 允许使用 lambda 表达式轻松实现:将 vector<_1> 作为类型传递,然后使用 typename mpl::apply<T, U>::type(其中 T 是模板参数),即可获得 vector<U>。 - Johannes Schaub - litb

1
解决方案是:使用函数重载而不是模板特化!编译时间将更好,限制消失...
请记住,任何模板都被认为在最接近的全局作用域中实例化,随后在使用后。这意味着这将起作用:
template <class T>
int f(const T& t)
{
  return g(t);
} 

int g(int a)
{
  return a+1;
}

int main()
{
   return f(5);
}

正如你所看到的,即使在函数g定义在f之后,它仍然可以工作。这意味着只要用户在使用模板之前定义了该函数,您就可以定义任何想要使用该函数的模板,C++会找到它。

还要记住,函数不存在模板特化!如果存在许多具有相同名称的函数,则C++始终期望重载,而不是模板特化。

简而言之,最好的解决方案是:只需像存在一样使用该函数,并期望用户在使用模板之前定义该函数。


问题已经使用了重载 - 答案没有回答问题。据我理解,他想要能够调用 f<vector>(), f<int>(), fboost::bind() 等等。 - James Hopkin
我不同意...他想为用户定义一个模板以便专门化它。然后,OP建议预定义函数模板。但是你不能专门化函数模板。此外,如果OP想要定义这个函数,那么这就是为了在他的代码中使用它。否则,OP应该提供更多关于如何使用该函数的细节。 - PierreBdR

0
template <class T> class myclass;

template <> class myclass<int>{};
template <> class myclass<std::vector<int>>{};
template <> class myclass<std::vector<std::vector<int> > >{};

这是你想要的吗?


不行。我想要能够在std::vector上专门化myclass,而不必实际给出std::vector的类型。你需要使用模板模板参数来实现这一点。 - Joseph Garvin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接