具有重复名称的类模板?

10

是否可以定义两个不同的模板类(通过模板参数数量)并使用相同的名称?

这是我正在尝试做的事情:

namespace MyNamespace
{

template<class TRet>
class FunctionObject
{
    typedef typename TRet ReturnType;
    virtual ReturnType const operator()() const = 0;
};


template<class TRet, class TArg0>
class FunctionObject
{
    typedef typename TRet ReturnType;
    typedef typename TArg0 FirstArgumentType;
    virtual ReturnType const operator()(FirstArgumentType const &arg) const = 0;
};

}

在第二个FunctionObject结构定义的闭合括号末尾,我收到一个提到模板参数过多的错误。

我知道在C#中可以完成这个操作,但不确定在C++中是否可以。有人能否解释一下?


1
C# 可以做什么?你是指模板吗?C# 没有模板。 - user405725
我认为他们在C#中指的是泛型。 - Hunter McMillen
@user460762 模板不是泛型。最好假设它们除了共享的一个用例之外没有任何共同点。(这并不意味着你尝试做的事情不能在C++中完成,只是“这可以在C#中完成”不相关。) - millimoose
@Inerdial 同意,我不是在比较功能,而只是为那些熟悉这两种语言的人提供一个清晰的理解。谢谢。 - Engineer007
4个回答

12

我认为部分特化可以解决问题:

namespace MyNamespace {

  template<class TRet, class TArg0>
  class FunctionObject
  {
      typedef typename TRet ReturnType;
      typedef typename TArg0 FirstArgumentType;
      virtual ReturnType const operator()(FirstArgumentType const &arg) const = 0;
  };

  template<class TRet>
  class FunctionObject<TRet,void>
  {
      typedef typename TRet ReturnType;
      virtual ReturnType const operator()() const = 0;
  };

}

你还可以从具有多个参数的主模板开始。

我认为C++11的可变参数模板使得这更加巧妙,但是我没有时间去尝试,所以最好让其他人来展示。


是的,我考虑过了,但如果我想创建另一个具有三个模板参数的类,那么我将不得不修改当前的类以包括不适用的void参数。不过这是个好建议。谢谢。 - Engineer007
@user460762:是的,你必须从你想要处理的最大参数数量开始。有些人通过依赖宏元编程来处理这个问题。但是,就像我说的那样,可变参数模板可以解决这个问题。 - sbi
1
为什么不只定义一个类模板,并为第二个模板参数提供默认参数呢? - Nawaz
1
@sbi 我加了一个使用你的变长模板建议的答案。 - bames53
1
@bames53:这些肯定不是我的!但我还是点了赞。:) - sbi
显示剩余2条评论

7
展示sbi提出的可变参数模板解决方案:
namespace MyNamespace {

  template<typename...> FunctionObject;

  template<class TRet, class TArg0>
  class FunctionObject<TRet,TArg0>
  {
      typedef typename TRet ReturnType;
      typedef typename TArg0 FirstArgumentType;
      virtual ReturnType const operator()(FirstArgumentType const &arg) const = 0;
  };

  template<class TRet>
  class FunctionObject<TRet>
  {
      typedef typename TRet ReturnType;
      virtual ReturnType const operator()() const = 0;
  };

}

现在,您可以按任何顺序添加专业化内容,而无需修改其他模板(除非模板参数的数量/类型发生冲突)。

是的,对于这个来说 +1 比我的代码更简单。如果你有支持它的编译器就可以了。 - sbi

5

我认为你可以使用一个类模板来使其工作,将第二个模板参数的默认类型参数设置为:

struct null_type {};

template<class TRet, class TArg0 = null_type>
class FunctionObject
{
    typedef typename TRet ReturnType;
    typedef typename TArg0 FirstArgumentType;

    //both functions here
    virtual ReturnType const operator()() const = 0;

    virtual ReturnType const operator()(FirstArgumentType const &arg) const = 0;
};

3
大致朝着正确的方向,但还没完全到位。这将允许使用一个或两个参数的模板,但在原始问题中,两个模板的定义似乎有很大的不同(不同成员,operator() 的签名不同),这表明特化可能是解决问题的方法。与sbi的答案相比,这个答案有一个好处,就是创建了一个独特的类型(null_type应该是独一无二的),从而允许在两个参数的特化中使用void作为第二个参数,这很好(+1)。 - David Rodríguez - dribeas
@David:这确实是我想法的一个严重缺陷,我应该修正它。 - sbi
@user460762:不需要每个类都实现不适用的运算符,只要在基类中添加默认的空实现,将它们变为非纯虚函数即可。 - Nawaz
@Nawaz:没错,但你必须从这些操作符中返回值,并且决定如何在基类中最好处理是有风险的,因为在默认情况下,你正在将应该最好返回的值的决策从声明模板类型的对象中拿走。 - Engineer007
1
我必须同意@user460762的观点,这并不优雅,因为如果您犯了错误,例如创建FunctionObject<int> fo并调用fo(null_type()),或者相反地创建FunctionObject<int,int> fo并调用fo(),编译器将无法帮助您。在C#版本(从中产生了这个想法)中,这将是一个编译时错误。 - David Rodríguez - dribeas
显示剩余2条评论

1

我相信像这样做也可以,但是拥有单独的类可能不是你想要的:

   namespace MyNamespace
   {

      class AbstractFunctionObject
      {
         //shared functionality here
      };

      template<class TRet>
      class ConcreteFunctionObjectA : AbstractFunctionObject
      {
         typedef typename TRet ReturnType;
         virtual ReturnType const operator()() const = 0;
      };


      template<class TRet, class TArg0>
      class ConcreteFunctionObjectB : AbstractFunctionObject
      {
         typedef typename TRet ReturnType;
         typedef typename TArg0 FirstArgumentType;
         virtual ReturnType const operator()(FirstArgumentType const &arg) const = 0;
      };

   }

虽然这似乎是可能的,但也毫无用处。一个空的多态基类有什么好处? - sbi
它不会是空的。如果 OP 选择这条路线,我想他会将所有可变参数模板共享的功能移动到基类中。很难猜测意图,所以我没有填写。 - Hunter McMillen
一个Functor中可能包含什么,除了operator()()和一些typedefs? - sbi

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