【问题标题】:Templated Member Function of Templated Class模板类的模板成员函数
【发布时间】:2013-01-25 09:27:01
【问题描述】:

我有一个模板化的 C++ 类,它也有一个模板化的成员函数。这个成员函数的模板参数以特定的方式依赖于类的模板参数(请看下面的代码)。 我正在为它的模板参数的两个不同值实例化(不是专门化)这个类。一切都编译到这一点。但是,如果我调用模板化成员函数,则仅对第一个实例化对象的调用会编译,而不是第二个。 似乎编译器没有为模板类的第二次实例化实例化模板化成员函数。我正在使用“g++ filename.cpp”编译下面的代码并收到以下错误:

filename.cpp:63: error: no matching function for call to 'Manager::init(Combination*)'

这是调用b.init(&combination_2)的行

g++ --version => g++ (Ubuntu/Linaro 4.4.7-1ubuntu2) 4.4.7

uname -a => Linux 3.2.0-25-generic-pae #40-Ubuntu SMP i686 i686 i386 GNU/Linux

enum Base {
  AA,
  BB,
  CC
};

enum Dependent1 {
  PP,
  QQ,
  RR
};

enum Dependent2 {
  XX,
  YY,
  ZZ
};

template<Base B>
struct DependentProperty {
};

template<>
struct DependentProperty<AA> {
  typedef Dependent1 Dependent;
};

template<>
struct DependentProperty<BB> {
  typedef Dependent2 Dependent;
};

template <Base B, typename DependentProperty<B>::Dependent D>
class Combination {
 public:
  void reset() {}
  int o;
};

template <Base B>
class Manager {
 public:
  template <typename DependentProperty<B>::Dependent D,
            template<Base,
                    typename DependentProperty<B>::Dependent> class T>
  void init(T<B, D>* t);
};

template <Base B>
template <typename DependentProperty<B>::Dependent D,
          template<Base,
                  typename DependentProperty<B>::Dependent> class T>
void Manager<B>::init(T<B, D>* t) {
  t->reset();
}

int main(int argc, char** argv) {
  Manager<AA> a;
  Manager<BB> b;
  Combination<AA, PP> combination_1;
  Combination<BB, XX> combination_2;
  a.init(&combination_1);
  b.init(&combination_2);
  return 0;
}

在我们的实际项目中,从我的示例代码中修改Base、Dependent或Combination对应的类是不可行的。我真正想知道的是我定义 Manager::init() 的语法是否错误,或者是否有一些已知的 C++ 或 g++ 的属性/功能/约束不允许此代码?

【问题讨论】:

  • 在定义模板函数 init() 之前使用typedef 会是明智的typename DependentProperty&lt;B&gt;::Dependent 重复了两遍,真的无助于理解。
  • 我不是 100% 确定,但这可能是编译器的错误

标签: c++ templates gcc


【解决方案1】:

下面的代码是为我编译的,我已经稍微简化了你的代码,虽然它仍然做同样的事情。

template <Base B>
class Manager {
 public:
typedef typename DependentProperty<B>::Dependent D;  // if ever you need it
    template <typename TCombinaison>
    void init(TCombinaison* t)
    {
        t->reset();
    }

};

int main(int argc, char** argv) 
{
    typedef Combination<AA, PP> CombinaisonA;
    typedef Combination<BB, XX> CombinaisonB;

    typedef DependentProperty<AA> DependencyPropertyA;
    typedef DependentProperty<BB> DependencyPropertyB;

  CombinaisonA combination_1;
  CombinaisonB combination_2;

  Manager<AA> a;
  Manager<BB> b;

  a.init(&combination_1);
  b.init<&combination_2);

  return 0;
}

编辑:第二个解决方案,以禁止在经理中混合使用组合,正如 OP 在下面的 cmets 中注意到的那样。现在我正在使用 std::is_same 来检查“概念”合同。

template <Base B, typename DependentProperty<B>::Dependent D>
class Combination {
 public:
    typedef typename DependentProperty<B>::Dependent DependencyType;
  void reset() {}
  int o;
};

template <Base B>
class Manager {
 public:
    typedef typename DependentProperty<B>::Dependent DependencyType; 
    template <typename TCombinaison>
    void init(TCombinaison* t)
    {
        static_assert(std::is_same<TCombinaison::DependencyType, Manager::DependencyType>);
        t->reset();
    }

};

【讨论】:

  • 它与我的预期代码不同,因为它允许 b.init(&combination_1) 也可以编译。我希望 init() 只接受与调用 init() 的 Manager 对象共享相同 Base 模板参数的 Combination 对象。
  • @user2010172 我已经相应地修改了代码,使用std::is_same 来检查DependencyProperties 是否兼容。
  • 使用 std::is_same 强制兼容,但与我的预期代码还有一个不同之处。在我的预期代码中, init() 可以进一步使用 Dependent 模板参数(可能会调用以该参数为模板的其他函数),而现在它不能。另一个小问题:现在我们要求每个 TCombinaison 候选者额外定义一个公共的 DependencyType 用于 std::is_same 检查。
  • @user2010172 你的结论有点太快了,说你不能再……确实可以在 init() 中访问 B 型和 D 型的知识。显然,您的编译器和我的编译器(此处为 VS2010)无法推断模板模板参数的类型。也许你应该尝试一些更明确的声明。
  • 你是对的,在 init() 中获取该信息是可能,但我可以在不修改所有现有的和其他满足要求的有效类的情况下获取它init() 的模板参数 T?
【解决方案2】:

如果您结合继承并摆脱常量模板参数,扩展组合以提供有关其模板参数的信息,您可以获得要编译的代码,考虑到您不希望它编译:

b.init(&combination_1);

您正在努力间接在您的 Manager 中指定和修复 init 成员模板的组合类型,即使 init 模板会推断它,因为它是唯一的参数函数,以及在 main 中定义的类型 si。

你会考虑直接用 Combination 模板化 init 吗?

这样,除了 init() 声明之外的所有内容都保持不变,并且您的代码将按照您最初想要的方式编译:

class Base
{
};

class AA
:
    public Base
{
};

class BB
: 
    public Base
{
};

class Dependent1
{
};

class PP
:
    public Dependent1
{};

class Dependent2
{};

class XX
:
    public Dependent2
{};

template<class Base>
struct DependentProperty {
};

template<>
struct DependentProperty<AA> {
  typedef Dependent1 Dependent;
};

template<>
struct DependentProperty<BB> {
  typedef Dependent2 Dependent;
};

template <class Base> 
class Combination {
 public:

     typedef Base CombinationBase;
     typedef typename DependentProperty<Base>::Dependent CombinationDependent;

     void reset() 
     {

     }

     int o;
};


template <class Base>
class Manager
{
    public:

        // Any type C
        template<class C>
        void init (C* t)
        {
            // Any type C conforming to the implicit interface holding reset()
            t->reset(); 
            // Forcing specific combination
            Base b = typename C::CombinationBase(); 
            // Forcing it again
            typename DependentProperty<Base>::Dependent d = typename C::CombinationDependent();
        }
};

int main(int argc, char** argv) {

  Combination<AA> combination_1;
  Manager<AA> a;
  a.init(&combination_1);

  Manager<BB> b;
  Combination<BB> combination_2;
  b.init(&combination_2);

  b.init(&combination_1);

  return 0;
}

在这种情况下,您可以扩展 Combination 模板,以向客户端代码提供对其模板参数的访问。当然,在这种情况下,只要您依赖它在 init 成员函数中的实现(访问存储的模板参数值等),模板 C 在这种情况下就会成为组合概念的改进。

【讨论】:

  • 这个建议确实是正确的,但是我在上面编辑了我原来的问题,以反映为什么我不能走这条路。
  • @user2010172 好的.. 我再看看.. 但奇怪的是第一个组合有效,而不是第二个...
【解决方案3】:

你的代码是正确的,除了函数调用部分。

a.init<PP, Combination>( &combination_1 );
b.init<XX, Combination> ( &combination_2 );

这会编译并和平运行。

【讨论】:

    【解决方案4】:

    我唯一看到的是

    template <typename DependentProperty<B>::Dependent D,
              template<Base, <-- wrong
                    typename DependentProperty<B>::Dependent <-- wrong
              > class T>
    void init(T<B, D>* t);
    

    你的类Combination 等待values作为模板参数,但你想给他types

    我花了一些时间来修复它 - 就像那样

    template <typename DependentProperty<B>::Dependent D,
              template<Base BB,
                    typename DependentProperty<BB>::Dependent DD
              > class T>
    void init(T<B, D>* t);
    

    还有很多其他变种,但都没有成功。

    对不起,我把它作为一个答案,但我无法在评论中输入这么多代码

    【讨论】:

    • template&lt;Base&gt;template&lt;Base BB&gt; 相同。也许你在想template&lt;typename Base&gt;
    • @BenVoigt 你是对的。但是typename DependentProperty&lt;B&gt;::Dependent 呢?模板会怎么想typename
    猜你喜欢
    • 2010-12-22
    • 2012-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-02
    • 1970-01-01
    相关资源
    最近更新 更多