2014-01-28 2 views
5

Размышляя, как CRTP может быть улучшен в C++ 11, я закончил со следующим кодом:шаблонов псевдонимов и зависимые имена

template <typename Derived, typename Delayer> 
struct derived_value_type 
{ 
    typedef typename Derived::value_type type; 
}; 

template <typename Derived> 
struct base 
{ 
    template <typename Delayer = void> 
    typename derived_value_type<Derived, Delayer>::type 
    foo(){ return {}; } 
}; 

struct derived : base<derived> 
{ 
    typedef int value_type; 
}; 

#include <iostream> 
#include <typeinfo> 

int main() 
{ 
    derived d; 
    auto bar = d.foo(); 

    std::cout << typeid(bar).name() << ':' << bar << std::endl; 
} 

Я считаю, что предыдущий код будет стандартными совместимым, и он компилирует и работает с основными компиляторами (в результате получается i:0). Однако, когда я использую шаблон псевдоним вместо этого, я получаю ошибку компиляции из-за derived будучи неполным:

template <typename Derived, typename Delayer> 
using derived_value_type = typename Derived::value_type; 

/*...*/ 

template <typename Delayer = void> 
derived_value_type<Derived, Delayer> 
foo(){ return {}; } 

Является ли это ошибка компилятора, или же тот факт, что компилятор может детерминированный, что нет никакого реальных зависимости с Delayer означает, что псевдоним шаблона не является зависимым типом? Где это указано в стандарте?

ответ

4

Шаблоны шаблонов и шаблоны функций создаются, но шаблоны псевдонимов просто заменяются. И, избавившись от имени участника type, вы теряете возможность вызывать правила поиска зависимых имен.

[N3285] 14.5.7p2:

Когда шаблон Идентификатор относится к специализации шаблона псевдонима, это равносильно тому, ассоциированного типа, полученного путем замены его шаблонов аргументов для шаблонов в тип-идентификатор шаблона псевдонима.

Таким образом, в первом случае, у вас есть:

Определение struct derived требует неявного экземпляра base<derived>. Во время этого экземпляра, мы открываем base<derived> есть шаблон функции члена:

template <typename Delayer=void> 
typename derived_value_type<derived, Delayer>::type foo(); 

Возвращаемый тип зависит, поэтому type еще не посмотрел, а не специализация derived_value_type не конкретизируется. Исполнение завершается, и base<derived> и derived оба являются теперь полными типами.

В main выражение требует неявного экземпляра base<derived>::foo<void>(). Теперь имя typename derived_value_type<derived, void>::type просматривается, создавая экземпляр derived_value_type<derived, void> по пути. Тип возвращаемого значения - int.

Во втором случае derived_value_type не является зависимым именем, поэтому привязывается к объявлению шаблона псевдонима при определении шаблона base<D>. Компилятор может сделать подстановку псевдоним либо в определении шаблона или во время каждого экземпляра класса, но в любом случае вы получите шаблон класса эквивалент:

template <typename Derived> 
struct base 
{ 
    template <typename Delayer = void> 
    typename Derived::value_type 
    foo(){ return {}; } 
}; 

определение struct derived требует неявного экземпляра base<derived>.Во время этого экземпляра, мы открываем base<derived> есть шаблон функции члена:

template <typename Delayer=void> 
typename derived::value_type foo(); 

Но derived::value_type не зависит, и derived неполный тип, поэтому код плохо сформирован.

Смежные вопросы