2015-04-06 8 views
4

У меня есть простая шаблонная структура-оболочка с функцией-членом, вызывающей .error() на объект его типа шаблона.decltype (auto) в функции-члена игнорирует недопустимый объект body, decltype (expr) не удается

template <typename T> 
struct Wrapper { 
    T t; 
    decltype(auto) f() { 
     return t.error(); // calls .error() 
    } 
}; 

Если я создаю экземпляр этого с типом, который не имеет функцию члена error(), это хорошо до тех пор, пока я не называю. Это поведение, которое я хочу.

Wrapper<int> w; // no problem here 
// w.error(); // uncommented causes compilation failure 

Если я использую то, что я думал, что семантический эквивалент с типом возвращаемого трейлинг, это ошибки в объявлении переменной

template <typename T> 
struct Wrapper { 
    T t; 
    auto f() -> decltype(t.error()) { 
     return t.error(); 
    } 
}; 

Wrapper<int> w; // error here 

Я принимаю, что два не семантически эквивалентны, но в любом случае, чтобы получить поведение первого, используя возвращаемый тип возврата (только для C++ 11), не специализируясь на целом классе с каким-то HasError tmp trickery?

+0

@dyp 'decltype (auto)' был добавлен в C++ 14. – Brian

+0

@Brian Ah, я был введен в заблуждение тегом C++ 14. Должен был читать более внимательно, спасибо. – dyp

+0

Если 't' является публичным элементом данных, почему бы не использовать свободную функцию (шаблон)? – dyp

ответ

5

Разница между версиями

decltype(auto) f(); 
auto f() -> decltype(t.error()); 

является то, что функция декларация второго может быть недействительным. Возврат типа вычитания для шаблонов функций происходит, когда задано определение [dcl.spec.auto]/12. Хотя я не мог найти ничего о выводе типа возвращаемого значения для функций-членов шаблонов классов, я думаю, что они ведут себя аналогичным образом.

Косвенно инстанцирование шаблона класса Wrapper приводит к конкретизации из деклараций, но не из определений всех (не виртуальных) функций членов [temp.inst]/1. В заявлении decltype(auto) f(); указан недействительный заполнитель, но он действителен. С другой стороны, auto f() -> decltype(t.error()); имеет недопустимый тип возврата для некоторых экземпляров.


Простым решением в C++ 11 является отсрочка определения типа возврата, например. поворотом f в шаблон функции:

template<typename U = T> 
auto f() -> decltype(std::declval<U&>().error()); 

Определение этой функции беспокоит меня немного, хотя:

template<typename U = T> 
auto f() -> decltype(std::declval<U&>().error()) 
{ 
    return t.error(); 
} 

Для специализаций Wrapper где t.error() не является действительным, выше f является который не может выдавать действительные специализации. Это может подпадать под [temp.res]/8, в котором говорится, что такие шаблоны плохо сформированный Нет Диагностические Обязательно:

Если действительная специализация не может быть сгенерирована для шаблона, и этот шаблон не создается экземпляр, шаблон плохо сформирован, не требуется диагностика .

Однако, я подозреваю, что правило было введено в позволяют, но не требуют, реализации, чтобы проверить на наличие ошибок, не являющихся инстанцирован шаблонов.В этом случае отсутствует ошибка программирования в исходном коде ; ошибка возникнет при создании экземпляров шаблона класса, описанного в исходном коде. Поэтому я думаю, что все должно быть хорошо.


Альтернативным решением является использование типа запасным возврата, чтобы сделать функцию декларации хорошо сформированным, даже если определение не является (для всех конкретизации):

#include <type_traits> 

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

template <typename T> 
struct Wrapper { 
    T t; 

    template<typename U=T, typename=void> 
    struct error_return_type_or_void : type_is<void> {}; 

    template<typename U> 
    struct error_return_type_or_void 
     <U, decltype(std::declval<U&>().error(), void())> 
    : type_is<decltype(std::declval<U&>().error())> {}; 

    auto f() -> typename error_return_type_or_void<>::type { 
     return t.error(); 
    } 
}; 
+0

Возможно, нужно добавить некоторые проверки ошибок, чтобы убедиться, что 'U == T', например. 'static_assert'. – dyp

+0

Я не думаю, что в этом случае применяется [temp.res]/8, так как при создании экземпляра класса шаблонная специализация только создает декларацию шаблона функции-члена, поскольку все, что у вас есть, - это объявление. Применяя в этом случае, это означает, что вы проверяете шаблоны функций-членов более строгие, чем простые функции-члены, и что использование 'std :: vector ' является плохо сформированным NDR (шаблон 'insert()' и 'assign () ', беря непередовые итераторы, требуется MoveInsertable/MoveAssignable), что почти наверняка не предусмотрено комитетом. –

+0

@ t.c. возможно, но намерения не оправдывают недопустимый код, если нет другой альтернативы. Я читал, что, как намереваясь разрешить больше проверок позже, но такие проверки могут не выходить за пределы шаблона, который coukd (в настоящее время) действителен. Поэтому, если мы в конечном итоге гарантируем, что шаблон всегда недействителен и добавит диагностику в будущую итерацию, у нас есть только сломанный код, который уже был плохо сформирован. – Yakk

2

Один из подходов - это метка crtp.

// todo: 
    template<class T> 
    struct has_error; // true_type if T.error() is valid 

    template<class D,class T,bool Test=has_error<T>{}> 
    struct do_whatever { 
    D* self(){return static_cast<D*>(this);} 
    D const* self()const{return static_cast<D const*>(this);} 

    auto f()->decltype(self()->t.error()) { 
     return self()->t.error(); 
    } 
    }; 
    template<class D,class T> 
    struct do_whatever<D,T,false>{}; 

Теперь f() просто не существует, если T не error().

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