2015-08-14 2 views
2

Рассмотрим следующие struct s:Использование нетиповых аргумент шаблона при частичной специализации

//Implementations provided elsewhere 

struct A { A(int i, double d, std::string s); /* ... */ }; 
struct B { B(double d1, double d2);   /* ... */ }; 

У меня есть два класса преобразования которых шаблон подписи выглядит следующим образом:

TupleAs< A, int, double, std::string > via1 { ... }; 
ArrayAs< B, double, 2 >    via2 { ... }; 

Предсказуемо, TupleAs преобразует триплет int, double и std::string значения в объект типа A. Аналогично, ArrayAs преобразует пару из двух значений double в объект типа B. (И да, есть причины, почему я не могу назвать в A и B конструкторов непосредственно.)

Улучшение синтаксиса

Я хотел бы изменить синтаксис, так что я могу сделать следующее:

TupleAs< A(int,double,std::string) > via1 { ... }; 
ArrayAs< B(double,2) >    via2 { ... }; 

который, я думаю, более подробно описывает процесс преобразования. TupleAs шаблон декларации и соответствующей частичной специализации будет выглядеть следующим образом:

template <typename T> struct TupleAs; 

template <typename T, typename ... Args> 
struct TupleAs<T(Args...)> { ... }; 

ошибки компилятора

Однако, если я пытаюсь сделать что-то подобное с версией ArrayAs:

template <typename T> struct ArrayAs; 

template <typename T, typename U, unsigned N> 
struct ArrayAs<T(U,N)> { ... }; 

I получить следующие ошибки в clang (3.6) при попытке его создания (ArrayAs< B(double,2)> test;):

typeAs.cpp:14:22: error: unknown type name 'N' 
    struct ArrayAs<T(U,N)>{ 
        ^
typeAs.cpp:14:10: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used 
    struct ArrayAs<T(U,N)>{ 
     ^~~~~~~~~~~~~~~ 
typeAs.cpp:13:45: note: non-deducible template parameter 'N' 
    template<typename T, typename U, unsigned N> 
              ^

Диагностика ошибок gcc немного отличается, но я не буду публиковать ее здесь.

Я признаю, что мои навыки шаблонирования должны быть лучше, чем они есть, и я также признаю, что аналогичная декларация std::function<B(double,2)> явно абсурдна. Но может ли кто-нибудь сказать мне, почему конкретный синтаксис, который я пытаюсь достичь, не разрешен? Я просмотрел стандарт C++ 14 и не смог найти соответствующую часть, и у меня возникли проблемы с интерпретацией диагностического сообщения clang.

+3

Вы специализируетесь на выборе типа функции. Тип функции имеет вид 'type (types ...)', нет места для целого числа. С другой стороны, синтаксис 'B (double, 2)' не имеет смысла в C++ в любом контексте. Сообщение об ошибке может, по общему признанию, иметь больше смысла. – Quentin

+0

Вам нужно, чтобы '2' был параметром времени компиляции? – sfjac

ответ

2

Когда вы специализируетесь TupleAs:

template <typename T, typename ... Args> 
struct TupleAs<T(Args...)> 

Вы в основном перегружать обозначения для функции. Вы специализируетесь на функции , которая принимает Args... и возвращает T. Это тип. Вы не можете использовать эту функцию как функцию или действительно думаете о ней как о типе, но это то, что она есть.

С другой стороны, здесь:

template <typename T, typename U, unsigned N> 
struct ArrayAs<T(U,N)> { ... }; 

Там нет такого понятия, как функция, которая принимает N. Это может занять unsigned, но оно не может принимать значение . Нет такой разумной вещи.Из вашего примера B(double, 2) просто не имеет смысла. В лучшем случае, вы могли бы написать что-то, что позволило бы:

template <unsigned N> using size_ = std::integral_constant<size_t, N>; 

ArrayAs< B(double,size_<2>) > 

Или даже:

ArrayAs< B(std::array<double, 2>) > 

поскольку теперь мы вернулись к использованию типов во всем мире. Предпочитаете ли вы это или нет, это личное предпочтение.

Ключевым моментом здесь является то, что типы являются первоклассными гражданами, когда дело доходит до метапрограммирования всех вещей, и, по возможности, следует избегать ценностей.

+0

К сожалению, 'ArrayAs ' не работает. – Yakk

+0

@Yakk 'ArrayAs ' does, but * yuck *. Blay array decay: p – Quentin

+1

@Yakk Я потерял слезу. – Barry

2
template <typename T> struct ArrayAs; 

template <typename T, typename U, std::size_t N> 
struct ArrayAs<T(std::array<U,N>)> { ... }; 

работы, как бы:

template<class T> 
struct to_array; 
template<class T, size_t N> 
struct to_array<T[N]> { using type = std::array<T, N>; }; 
template<class T> 
using arr = typename to_array<T>::type; 

затем:

ArrayAs< Bob(arr<int[3]>) > some_var; 

live example.

К сожалению, непосредственно с использованием ArrayAs< Bob(int[3]) > не работает из-за того, как массивы в типах функций распадаются на указатели.

+0

Я не думал о попытке синтаксиса подстроки массива. Спасибо за это. Но, увы, из-за распада ... – KyleKnoepfel