Wednesday, 20 September 2017

c++ - Inheriting typedefs?

Answer


Answer





I've been confused recently by a few code examples -- sometimes it seems that inheriting typedefs exposed by a base class works, and sometimes it seems that it doesn't.



My questions are





  • Why doesn't it always work?

  • What are the situations that it will / won't work?

  • What are the good workarounds when it doesn't work?



Here's some specific code:



// First example: Inheriting `static const int ...`

// Basic TypeList object
template
struct TypeList {
static const int size = sizeof...(Ts);
};

// Repeat metafunction
template
struct repeat;


template
struct repeat> : TypeList {};

// Checks
typedef TypeList MyList;

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



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



// template ::func_ptr_type me>
// struct FuncPtr : FuncPtrTypes {
// 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::result_type’ was intended)



// This compiles but is clumsy:

template ::func_ptr_type me>
struct FuncPtr {
typedef typename FuncPtrTypes::input_type input_type;
typedef typename FuncPtrTypes::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() {}

Answer



We can simplify your failing example down to:




template 
struct Base { using type = T; };


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



The issue is that type here is a dependent name. It depends on T. There is no guarantee that for a given T there isn't some specialization of Base which does not name type. As such, base templates of class templates are not part of the standard name lookup, so you have to qualify it:



Base::type mem;


Although now we run afoul of the rule which indicates that dependent names are not assumed to be types unless explicitly states as such, so you need:



typename Base::type mem;



None of the other cases presented in the OP rely upon unqualified lookup of a dependent name.



To return to the specific problem, this doesn't compile:



static result_type apply(input_type i) {


because result_type and input_type are dependent types and so must be qualified and prefixed with typename:




static typename FuncPtrTypes::result_type apply(typename FuncPtrTypes::input_type i) {


Or, if you prefer, you can simply bring both names in with a using-declaration:



 using typename FuncPtrTypes::input_type;
using typename FuncPtrTypes::result_type;

static result_type apply(input_type i) {


No comments:

Post a Comment

casting - Why wasn't Tobey Maguire in The Amazing Spider-Man? - Movies & TV

In the Spider-Man franchise, Tobey Maguire is an outstanding performer as a Spider-Man and also reprised his role in the sequels Spider-Man...