存储指向同一模板类的模板类型的指针

Posted

技术标签:

【中文标题】存储指向同一模板类的模板类型的指针【英文标题】:Storing a pointer to a template type of the same template class 【发布时间】:2016-02-26 02:17:19 【问题描述】:

假设我们有以下内容:

template<typename A, typename B>
class Foo 
private:
    A m_a;
    B m_b;
    Foo<A,B>* m_pFoo;

public:
    Foo( A a, B b, Foo<A,B>* pFoo = nullptr );
;

有了这个类,我可以将任何pFoo 保存到m_pFoo,只要实例化 的类型匹配如下:

 int main() 
     Foo<int, int> foo1( 3, 5 );
     Foo<int, int> foo2( 2, 4, &foo1 ); // This Works
     Foo<int, int> foo3( 5, 7, &foo2 ); // This Still Works 

     Foo<double, int> foo4( 2.4, 5 );
     Foo<double, int> foo5( 7.5, 2, &foo4 ); // This Works
     Foo<double, int> foo6( 9.2, 6, &foo5 ); // This Still Works

     // Compiler Error - Can not deduce template arguments
     Foo<double, int> foo7( 3.7, 2, &foo1 ); // Doesn't Work

     return 0;
 

在上一个问题中,我演示了一个与此类似的问题,我最初的问题是如何将指向类模板类型的指针传递给相同的类模板构造函数,但是我收到的一个关于此的响应是传入指针不是问题,但存储是。所以有了这篇文章,我的新问题变成了:

我如何能够拥有与上述每种类型相同或相似的构造函数的相同类模板:

Foo<short, short>           ssFoo;
Foo<short, int>             siFoo;
Foo<short, int64_t>         si64Foo;
Foo<short, unsigned>        suFoo;
Foo<short, float>           sfFoo;
Foo<short, double>          sdFoo;
Foo<short, long>            slFoo;
Foo<short, long long>       sllFoo;

Foo<int, short>             isFoo;
Foo<int, int>               iiFoo;
Foo<int, int64_t>           ii64Foo;
Foo<int, unsigned>          iuFoo;
Foo<int, float>             ifFoo;
Foo<int, double>            idFoo;
Foo<int, long>              ilFoo;
Foo<int, long long>         illFoo;

Foo<int64_t, short>        i64sFoo;
Foo<int64_t, int>          i64iFoo;
Foo<int64_t, int64_t>      i64i64Foo;
Foo<int64_t, unsigned>     i64uFoo;
Foo<int64_t, float>        i64fFoo;
Foo<int64_t, double>       i64dFoo;
Foo<int64_t, long>         i64lFoo;
Foo<int64_t, long long>    i64llFoo;

Foo<unsigned, short>        usFoo;
Foo<unsigned, int>          uiFoo;
Foo<unsigned, int64_t>      ui64Foo;
Foo<unsigned, unsigned>     uuFoo;
Foo<unsigned, float>        ufFoo;
Foo<unsigned, double>       udFoo;
Foo<unsigned, long>         ulFoo;
Foo<unsigned, long long>    ullFoo;

Foo<float, short>           fsFoo;
Foo<float, int>             fiFoo;
Foo<float, int64_t>         fi64Foo;
Foo<float, unsigned>        fuFoo;
Foo<float, float>           ffFoo;
Foo<float, double>          fdFoo;
Foo<float, long>            flFoo;
Foo<float, long long>       fllFoo;

Foo<double, short>          dsFoo;
Foo<double, int>            diFoo;
Foo<double, int64_t>        di64Foo;
Foo<double, unsigned>       duFoo;
Foo<double, float>          dfFoo;
Foo<double, double>         ddFoo;
Foo<double, long>           dlFoo;
Foo<double, long long>      dllFoo;

Foo<long, short>            lsFoo;
Foo<long, int>              liFoo;
Foo<long, int64_t>          li64Foo;
Foo<long, unsigned>         luFoo;
Foo<long, float>            lfFoo;
Foo<long, double>           ldFoo;
Foo<long, long>             llFoo;
Foo<long, long long>        l_llFoo;

Foo<long long, short>       llsFoo;
Foo<long long, int>         lliFoo;
Foo<long long, int64_t>     lli64Foo;
Foo<long long, unsigned>    lluFoo;
Foo<long long, float>       llfFoo;
Foo<long long, double>      lldFoo;
Foo<long long, long>        ll_lFoo;
Foo<long long, long long>   ll_llFoo;

在将前一个实例的地址传递给新实例的构造函数的构造时,是否所有有效类型都存储在类模板中?还;我如何能够阻止此类接受任何自定义或用户定义的对象或字符、字符串类型、枚举和布尔类型?我希望 typenames 仅作为数字类型传递到类模板参数列表中。

【问题讨论】:

尽管都被命名为Foo,但它们都是不同的类型。如果你想创建一个Foo 的多态基类,你可能能够实现你想要的,这取决于类的特征有多少取决于模板参数。我认为没有一种类型安全的方法可以在类中任意存储任何对象并记住它是什么。不过,使用&lt;type_traits&gt;static_assert 来限制类型很容易。 你没有说出你想用这些指针做什么。因此,如果类型匹配,请快速测试,将指针更改为void*,然后完成。没用,因为你几乎不能用void* 做任何事情,但你没有问“然后用它做点什么”:你只是要求存储它。可能这是一个 XY 问题。或者,也许您确实想存储 void* 并且什么都不做。 我知道它们是不同的类型;那不是问题。我试图弄清楚如何能够保存不同的类型。我了解&lt;int,int&gt;&lt;int, float&gt;&lt;double, unsigned&gt; 不同。作为类模板的类使我不必为每种不同的类型编写相同的类 30 到 50 次,并且这些类型中的任何一种都对最后一个参数有效。问题是存储指向此类模板可以生成的任何有效类型的指针,并将它们保存在构造函数指针不是 nullptr 的类的实例中。 @Yakk 考虑处理多项式的数学:多项式具有例如项:2x、3、5x^2、3x^4*2y^5 都是多项式的单项。该术语的两个类型名称是 这就是为什么它们中的任何一个都只能是任何数字类型。考虑这个术语 3x^(5x^2) 这仍然是一个术语; x^(2x^(2y^3)) 也是如此。所以在我原来的 3x^(5x^2) 来源中:Term&lt;int,int&gt; termExpression( 5, 'x', 2, /*nullptr*/ );Term&lt;int, int&gt; term( 3, 'x', 1, &amp;termExpression );。如果存储的指针为空,则术语完成,... @Yakk (.​​..continued) 否则指针是另一个项,它是当前项指数的一部分。我希望能够表达这个: 3.2x^(2x^4.5) 所以代码中的这个是:Term&lt;unsigned, double&gt; expression( 2, 'x', 4.5 ); Term&lt;float, int&gt; term( 3.2f, 'x', 1, &amp;expression ); 这是我原来的问题与实际课程的链接。 ***.com/questions/35621908/… 【参考方案1】:

模板的实例是完全不同的类型,与所有其他类型和模板的其他实例分开。

Foo<short, short>

Foo<int, int>

是两个不同的类,它们之间的区别就像

Foo1;

Foo2;

彼此不同。

这些是不同的类。如下:

Foo<short, short> *m_pFoo;

Foo<int, int> *m_pFoo;

彼此之间也各不相同

Foo1 *m_pFoo;

Foo2 *m_pFoo;

是。 C++ 就是不能这样工作。模板的m_pFoo 成员只能指向一个类。你必须选择它是哪一个。它可以是与其自己的类相同的类型,这是一种选择。或者,它可以指向某个其他类的实例。但它只能是一个类。

当然,除非你做到了

void *m_pFoo;

但是,当然,走这条路,你会失去类型安全和类型检查。

正如在 cmets 中提到的,您也许可以从超类派生模板,并存储指向超类的指针:

class FooBase 

// ...

;

template<typename A, typename B> class Foo : public FooBase 

    A m_a;
    B m_b;
    FooBase* m_pFoo;

public:
    Foo( A a, B b, FooBase* pFoo = nullptr );
;

因此,您可以将指向任何Foo&lt;A, B&gt; 的指针传递给构造函数,该构造函数将自动转换为超类,并且仅存储指向超类的指针。

当然,这种方法还有许多其他含义,但这将是我能想到的最干净、最类型安全的方法——至少到目前为止。

【讨论】:

如果我们知道 OP 想要做什么,确切地说,使用数据,类型擦除是另一种选择(而不是继承)。它允许您为不相关的类型创建一个临时接口。 @Yakk 这里是我原始问题的链接,其中包含我正在使用的实际课程。 ***.com/questions/35621908/… @francis 如果您只想显示,然后扔掉所有垃圾并存储一个字符串。同样,您想要做什么对理想的解决方案很重要。 存储它们之后没有描述你想要做什么,即使是粗略的,也意味着人们陷入了猜测。求月也会给你不好的答案。【参考方案2】:

让我们先解决编译器错误以及它发生的原因: 无法推断出模板参数的原因是构造函数接受了指向Foo&lt;A, B&gt; 类型的指针。当您定义 foo7 时,您创建了一个 Foo&lt;double, int&gt; 类型的 foo7。然后,您尝试将 foo1 的引用传递给其类型为 Foo&lt;int, int&gt; 的参数,但构造函数需要 Foo&lt;double, int&gt;

为了解决这个问题,可以创建第三种模板类型,以便能够接受任何其他类型的Foo,同时不限于当前正在构造的类型。如果措辞不好,我深表歉意。我写了一个例子:

template<typename A, typename... Rest>
class Foo;

template<typename A, typename B, typename C>
class Foo<A, B, C>
private:
    A m_a;
    B m_b;
    C* m_pFoo;

public:
    Foo(A a, B b, C* p_Foo);
;

template<typename A, typename B>
class Foo<A, B>
private:
    A m_a;
    B m_b;
    Foo<A, B>* m_pFoo;

public:
    Foo(A a, B b, Foo<A, B>* p_Foo = nullptr);
;

在这里,我们声明了一个类 Foo,它可以接受 1 个或多个模板参数。在此之后,有一个 Foo 的定义,它接受三个模板参数,一个 Foo 只接受两个模板参数。 通过三参数定义,我们有了第三种类型,即 m_pFoo 指针的类型。在双参数定义中,我们将 m_pFoo 声明为 Foo&lt;A, B&gt;* 类型(这是您原始代码的初始行为)

int main() 
    Foo<int, int> foo1(3, 5);
    Foo<int, int, decltype(foo1)> foo2(2, 4, &foo1); 
    Foo<int, int, decltype(foo2)> foo3(5, 7, &foo2); 

    Foo<double, int> foo4(2.4, 5);
    Foo<double, int, decltype(foo4)> foo5(7.5, 2, &foo4);
    Foo<double, int, decltype(foo5)> foo6(9.2, 6, &foo5); 

    //Now works.
    Foo<double, int, decltype(foo1)> foo7(3.7, 2, &foo1); 

    return 0;
 

现在我们可以使用 decltype(在 C++11 中添加)将 foo 实例的类型作为模板参数传入。

这不是“自动”的,仍然需要您明确说明要传递给构造函数的 foo 的类型,但我认为这应该非常接近您想要做的。

【讨论】:

我在 Win7 64bit 上使用 VS2015 CE,我按照你的结构,无法编译。 这些以及更多:1>c:\users\skilz80\documents\visual studio 2015\projects\***solutions\readintegers\storage.h(114):错误 C2988:无法识别的模板声明/定义1>c:\users\skilz80\documents\visual studio 2015\projects\***solutions\readintegers\storage.h(114):错误 C2059:语法错误:'' 1>c:\users\skilz80\documents \visual studio 2015\projects\***solutions\readintegers\storage.inl(30): error C2039: 'ctor': is not a member of 'Foo' 但是,当我在第一个声明的类名之前添加关键字class 时;我没有收到这些错误。 修复后一切似乎现在都可以编译了;但我必须用我的类的函数运行一些测试。 哦,是的,class 关键字需要在那里。我直接从我的编辑器中复制了它,但是在为 SO 代码块格式化它时以某种方式删除了它。哎呀。我会编辑答案来解决这个问题。

以上是关于存储指向同一模板类的模板类型的指针的主要内容,如果未能解决你的问题,请参考以下文章

指向模板类的指针

模板化函数或带有指向基类的指针的函数

包含指向派生模板类的基类指针的类的赋值运算符和复制构造函数

部分模板类中不允许指向不完整类类型的指针

指向任意类方法的模板非类型指针

模板类的智能指针?