5

这是 foo 类:

template <typename T>
struct foo
{
    foo()
    {
        t = nullptr;
    }

    foo(T* p, bool flag)
    {
        t = p;
    }
private:
    T* t;
};

这里是类栏:

template <typename T>
struct bar: public foo<T>
{
    using foo<T>::foo<T>;
};

继承构造函数的语法是否正确?如果我使用“使用 foo::foo;” 然后 Visual C++ 2010 的编译器死掉了。那么基本上如何从 VC++ 2010 中的模板类继承构造函数?

4

4 回答 4

9
template <typename T>
struct bar: public foo<T>
{
    using foo<T>::foo<T>;
};

为了让这个解析正确,您需要在,template之前插入foo<T>;,以告诉编译器foo将被视为模板名称(它无法查看foo<T>告诉自己,因为T它是未知的)。但是 using::template声明中不允许使用 using。该名称也不是指 的所有构造函数bar:相反,它指的是这样一个构造函数的特定构造函数模板特化(T是模板参数),如下所示

template<typename T>
foo();

此外, using 声明使用template-id(like foo<T>) 作为其名称是无效的(这实际上禁止它引用函数模板特化,另外还声明了对名称转换函数模板特化的禁止),所以即使您使用::template(如果可能的话) 纠正解析问题,此时您仍然会出错。

引入继承的构造函数时,添加了允许使用句法规则引用构造函数的特殊规则:如果您有一个qualified-id(基本上是一个使用的限定名称...::...),并且最后一个限定部分之前的最后一个限定名称是一个特定的类,那么您可以通过另外两种方式来表示该类的构造函数:

  • 如果该类是使用模板ID(表单的名称foo<T>)命名的,并且最后一部分与模板名称匹配(因此,或者foo<T>::foo作为模板模板参数)。TTP<T>::TTPTTP
  • 如果最后一部分与类名匹配(所以,foo::fooT::TT作为模板参数)。

这两个附加规则仅在 using 声明中有效。它们自然不会出现在 C++03 中。C++03 中也存在的另一条规则是:如果最后部分命名注入的类名,那么这个限定名也指构造函数:

  • foo::foo会因此工作。但是仅凭此规则,T::T(其中T表示 class foo)将不起作用,因为foo没有称为 的成员T

因此,有了特殊规则,您可以编写

using foo<T>::foo;
using bar::foo::foo; // valid too

第二个也是有效的:foo是注入到基类中foo<T>并继承到的注入类名bar。我们用 引用该名称bar::foo,然后添加最后一部分foo,再次引用注入的类名,以表示 `foo.

现在您明白了为什么您尝试的初始名称会引用构造函数模板特化(如果允许的话):因为该foo<T>::foo部分将命名所有构造函数,<T>然后将过滤掉模板并传递类型争论。

于 2011-03-23T21:48:17.450 回答
6

如果您的编译器还不支持继承构造函数,但支持可变参数宏、可变参数模板和右值引用,以及非常方便的 type_trait,那么这里有一个非常不错的解决方法:

#include <type_traits>
#include <utility>
#include <ostream>

enum Color {Red, Blue};

#define USING(Derived, Base)                                 \
    template<typename ...Args,                               \
             typename = typename std::enable_if              \
             <                                               \
                std::is_constructible<Base, Args...>::value  \
             >::type>                                        \
    Derived(Args &&...args)                                  \
        : Base(std::forward<Args>(args)...) { }              \


template<typename Mixin>
class add_color
: public Mixin
{
    Color color;

public:
    USING(add_color, Mixin);

    friend std::ostream& operator<<(std::ostream& os, const add_color& x)
    {
        switch (x.color)
        {
        case Red:
            os << "Red";
            break;
        case Blue:
            os << "Blue";
            break;
        }
        os << ' ' << x.first << ' ' << x.second;
        return os;
    }
};

#include <string>
#include <iostream>

int main()
{
    add_color<std::pair<std::string, int>> x1("five", 5);
    std::cout << "x1 = " << x1 << '\n';
    add_color<std::pair<std::string, int>> x3;
    std::cout << "x3 = " << x3 << '\n';
    add_color<std::pair<std::string, int>> x4 = x1;
    std::cout << "x4 = " << x4 << '\n';
    std::pair<std::string, int> p;
    add_color<std::pair<std::string, int>> x5 = p;
    std::cout << "x5 = " << x5 << '\n';
}

如果你还没有 is_constructible,基本思想没有它也行,但是“继承的构造函数”会过于贪婪。

于 2011-03-23T21:42:37.063 回答
3

您不需要第二个模板参数;

template <typename T>
struct bar: public foo<T>
{
    using foo<T>::foo;
};

应该做

编辑我撤回这适用于 g++-4.4.1,但是当该功能可用时,这应该是正确的语法

于 2011-03-23T20:32:07.913 回答
2

其他答案已经很好地解释了 C++0x 中的继承构造函数是如何工作的。但是,在撰写本文时,还没有编译器完全实现了整个 C++0x 功能集。不幸的是,这意味着 VC++ 2010 还不支持继承构造函数。

C++0x 标准尚未发布。该标准的最终草案将在 3 月的某个时候完成,但 ISO 还需要几个月的时间才能发布它。在此期间,编译器编写者正在推出功能,以便在标准最终确定时尽可能地兼容 C++0x。

我相信最新版本的 GCC 支持继承构造函数,所以如果你现在必须尝试一下,你可以使用它。当然,C++0x 支持是实验性的,可能会随着发现错误等而改变。

于 2011-03-23T20:35:43.250 回答