继承typedef?

时间:2015-08-28 12:01:53

标签: c++ templates c++11 inheritance template-meta-programming

最近我被一些代码示例搞糊涂了 - 有时看起来继承基类暴露的typedef有效,有时它似乎没有。

我的问题是

  • 为什么它不能一直有效?
  • 它会/不会起作用的情况是什么?
  • 什么是不起作用的好解决方法?

以下是一些具体代码:

// First example: Inheriting `static const int ...`
// Basic TypeList object
template<typename... Ts>
struct TypeList {
    static const int size = sizeof...(Ts);
};

// Repeat metafunction
template<typename T>
struct repeat;

template<typename... Ts>
struct repeat<TypeList<Ts...>> : TypeList<Ts..., Ts...> {};

// Checks
typedef TypeList<int, float, char> MyList;

static_assert(MyList::size == 3, "D:");
static_assert(repeat<MyList>::size == 6, "D:");


// Second example: Inheriting typedefs
// Meta function to compute a bundle of types
template <typename T>
struct FuncPtrTypes {
    typedef int result_type;
    typedef T input_type;
    typedef result_type(*func_ptr_type)(input_type);
};


// template <typename T, typename FuncPtrTypes<T>::func_ptr_type me>
// struct FuncPtr : FuncPtrTypes<T> {
//     static result_type apply(input_type i) {
//         return me(i);
//     }
// };
//
// Doesn't compile (?): clang 3.6:
// main.cpp:34:9: error: unknown type name 'result_type'
//         static result_type apply(input_type i) {
//                ^
// main.cpp:34:27: error: unknown type name 'input_type'
//         static result_type apply(input_type i) {
//                                  ^
//
// g++ 4.8.4:
// main.cpp:34:9: error: ‘result_type’ does not name a type
//   static result_type apply(input_type i) {
//          ^
// main.cpp:34:9: note: (perhaps ‘typename FuncPtrTypes<T>::result_type’ was intended)


// This compiles but is clumsy:

template <typename T, typename FuncPtrTypes<T>::func_ptr_type me>
struct FuncPtr {
    typedef typename FuncPtrTypes<T>::input_type input_type;
    typedef typename FuncPtrTypes<T>::result_type result_type;

    static result_type apply(input_type i) {
        return me(i);
    }
};


// A non-template example:
struct foo {
    typedef int bar;
};

struct baz : foo {};

typedef baz::bar bazbar;
// ^ This compiles... huh??

int main() {}

2 个答案:

答案 0 :(得分:6)

我们可以将您的失败示例简化为:

template <typename T>
struct Base { using type = T; };


template <typename T>
struct Derived : Base<T>
{
    type mem; // error: 'type' does not name a type
};

问题是type这里是依赖名称。 取决于 T。对于给定的T,我们无法保证Base<T>没有名称为type的专业化。因此,类模板的基本模板不是标准名称查找的一部分,因此您必须对其进行限定:

Base<T>::type mem;

虽然现在我们违反了规则,该规则表明除非明确说明,否则不会将依赖名称视为类型,因此您需要:

typename Base<T>::type mem;

OP中提出的其他案例都不依赖于对从属名称的非限定查找。

要回到具体问题,这不会编译:

static result_type apply(input_type i) {

因为result_typeinput_type是相关类型,因此必须限定并以typename为前缀:

static typename FuncPtrTypes<T>::result_type apply(typename FuncPtrTypes<T>::input_type i) {

或者,如果您愿意,可以使用 using-declaration 简单地输入两个名称:

 using typename FuncPtrTypes<T>::input_type;
 using typename FuncPtrTypes<T>::result_type;

 static result_type apply(input_type i) {

答案 1 :(得分:2)

下面的代码将编译:

template <typename T, typename FuncPtrTypes<T>::func_ptr_type me>
 struct FuncPtr : FuncPtrTypes<T> {
     static typename FuncPtrTypes<T>::result_type apply(typename FuncPtrTypes<T>::input_type i) {
         return me(i);
     }
 };

问题基本上是c ++模板在编译期间有两个阶段的查找。第一阶段是语法查找,而第二阶段根据实际类型执行编译。

进一步扩展:

问题是result_type可能是全局符号,也可能是继承的,在查找的第一阶段,假设它是全局的,因为依赖于T的类型不会被解析/评估编制阶段/阶段。

请参阅:where typenames are required

您问题的另一个可能的解决方案是使用特征(例如):

template <class T>
struct FuncPtrTraits
{
  typedef int result_type;
  typedef T input_type;
  typedef result_type(*func_ptr_type)(input_type);
};

 template <typename T, typename TraitsT = FuncPtrTraits<T> >
 struct FuncPtr
     static typename TraitsT::result_type apply(typename TraitsT::input_type i) {
         return me(i);
     }
 };

如果设计正确,当你从基本特征类型继承时,特征可以帮助人们从外部修改代码,也可以部分修改代码。