Значения по умолчанию в шаблонах с аргументами шаблона (C ++) - PullRequest
27 голосов
/ 14 марта 2011

Предположим, у меня есть шаблон (называемый ExampleTemplate), который принимает два аргумента: тип контейнера (например, список, вектор) и содержащийся тип (например, float, bool и т. Д.). Поскольку контейнеры на самом деле являются шаблонами, этот шаблон имеет параметр шаблона. Вот что я должен был написать:

#include <vector>
#include <list>

using namespace std;

template < template <class,class> class C, typename T>
class ExampleTemplate {
    C<T,allocator<T> > items;
public:
    ....
};

main()
{
    ExampleTemplate<list,int> a;
    ExampleTemplate<vector,float> b;
}

Вы можете спросить, что такое «распределитель». Ну, изначально я попробовал очевидное ...

template < template <class> class C, typename T>
class ExampleTemplate {
    C<T> items;
};

... но я, к сожалению, обнаружил, что аргумент по умолчанию для распределителя ...

   vector<T, Alloc>
   list<T, Alloc>
   etc

... должен быть явно "зарезервирован" в объявлении шаблона. Это, как вы можете видеть, делает код более уродливым и заставляет меня воспроизводить значения по умолчанию аргументов шаблона (в данном случае - распределителя).

Что является ПЛОХОЙ .

РЕДАКТИРОВАТЬ: Вопрос не в конкретной проблеме контейнеров, а в «значениях по умолчанию в шаблонах с аргументами шаблона», и приведенное выше является лишь примером. Ответы, зависящие от знания того, что контейнеры STL имеют тип «:: value_type», не те, что мне нужны. Подумайте об общей проблеме: если мне нужно использовать аргумент шаблона C в шаблоне ExampleTemplate, а затем в теле ExampleTemplate, должен ли я воспроизводить аргументы по умолчанию для C, когда я его использую? Если у меня есть до, разве это не приводит к ненужному повторению и другим проблемам (в этом случае, когда C - контейнер STL, проблемы с переносимостью - например, «распределитель»)?

Ответы [ 6 ]

14 голосов
/ 14 марта 2011

Возможно, вы бы предпочли это:

#include <vector>
#include <list>

using namespace std;

template <class Container>
class ForExamplePurposes {
    typedef typename Container::value_type T;
    Container items;
public:
};

int main()
{
    ForExamplePurposes< list<int> > a;
    ForExamplePurposes< vector<float> > b;
}

При этом используется "static duck typing ".Он также немного более гибок, так как не заставляет тип Container поддерживать концепцию STL Allocator.


Возможно, использование черты типа может дать вам выход:

#include <vector>
#include <list>

using namespace std;

struct MyFunkyContainer
{
    typedef int funky_type;
    // ... rest of custom container declaration
};

// General case assumes STL-compatible container
template <class Container>
struct ValueTypeOf
{
    typedef typename Container::value_type type;
};

// Specialization for MyFunkyContainer
template <>
struct ValueTypeOf<MyFunkyContainer>
{
    typedef MyFunkyContainer::funky_type type;
};


template <class Container>
class ForExamplePurposes {
    typedef typename ValueTypeOf<Container>::type T;
    Container items;
public:
};

int main()
{
    ForExamplePurposes< list<int> > a;
    ForExamplePurposes< vector<float> > b;
    ForExamplePurposes< MyFunkyContainer > c;
}

Тот, кто хочет использовать ForExamplePurposes с контейнером, не совместимым с STL, должен будет специализировать класс черт ValueTypeOf.

5 голосов
/ 14 марта 2011

Я бы предложил создать адаптеры.

Ваш класс должен быть создан с точным уровнем персонализации, который требуется для класса:

template <template <class> C, template T>
class Example
{
  typedef T Type;
  typedef C<T> Container;
};

РЕДАКТИРОВАТЬ: попыткапредоставить больше - это хорошо, но обречено на провал, посмотрите на различные расширения:

  • std::vector<T>: std::vector<T, std::allocator<T>>
  • std::stack<T>: std::stack<T, std::deque<T>>
  • std::set<T>: std::set<T, std::less<T>, std::allocator<T>>

Второй является адаптером и поэтому не использует распределитель, а третий не имеет такой же арности.Поэтому вам необходимо возложить ответственность на пользователя.

Если пользователь желает использовать его с типом, который не учитывает выраженную арность, то для него самый простой способ - предоставить (локально)адаптер:

template <typename T>
using Vector = std::vector<T>; // C++0x

Example<Vector, bool> example;

Меня интересует использование здесь пакетов параметров (шаблонов с переменными параметрами) ... Я не знаю, если объявление C как template <class...> C подойдет или еслитогда компилятору потребуется класс с переменным значением.

3 голосов
/ 29 февраля 2012

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

template <typename T, template <class U, class V = allocator<U> > class C>
class ExampleTemplate {
    C<T> items;
public:
    ....
};

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

// Other specialization failed. Instantiate a std::vector.
template <typename T, typename C>
struct make_container_
{
    typedef std::vector<T> result;
};

// STL containers
template <typename T, template <class U, class V = allocator<U> > class C>
struct make_container_<T,C>
{
    typedef C<T> result;
};

// Other specializations
...

template <typename T, typename C>
class ExampleTemplate {
    make_container_<T,C>::result items;
public:
    ....
};
1 голос
/ 15 марта 2011

Думаю, требуется воспроизвести все параметры шаблона, даже стандартные. Обратите внимание, что сам стандарт не использует параметры шаблона шаблона для адаптеров контейнера и предпочитает использовать стандартные параметры шаблона:

template < class T , class Container = deque <T > > class queue { ... };
template < class T , class Container = vector <T>, class Compare = less < typename Container :: value_type > > class priority_queue { ... };
0 голосов
/ 29 апреля 2015

Поскольку вопрос точно описал проблему, с которой я столкнулся в своем коде (я использую Visual Studio 2015), я нашел альтернативное решение, которым хотел бы поделиться.

Идея заключается в следующем: вместо передачи параметра шаблона шаблона в шаблон класса ExampleTemplate можно также передать обычное имя типа, содержащее тип DummyType в качестве фиктивного параметра, скажем std::vector<DummyType>.

Затем внутри класса один фиктивный параметр заменяется чем-то разумным.Для замены типа могут использоваться следующие вспомогательные классы:

// this is simply the replacement for a normal type:
// it takes a type T, and possibly replaces it with ReplaceByType
template<typename T, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type
{
    using type = std::conditional_t<std::is_same<T, ReplaceWhatType>::value, ReplaceByType, T>;    
};

// this sets up the recursion, such that replacement also happens
// in contained nested types
// example: in "std::vector<T, allocator<T> >", both T's are replaced
template<template<typename ...> class C, typename ... Args, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type<C<Args ...>, ReplaceWhatType, ReplaceByType>
{
    using type = C<typename replace_type<Args, ReplaceWhatType, ReplaceByType>::type ...>;
};

// an alias for convenience
template<typename ... Args>
using replace_type_t = typename replace_type<Args ...>::type;

Обратите внимание на рекурсивный шаг в replace_type, который учитывает, что типы, вложенные в другие классы, также заменяются - например, этимв std::vector<T, allocator<T> > заменены оба T, а не только первый.То же самое касается нескольких иерархий вложений.

Далее вы можете использовать это в своем ExampleTemplate -классе,

struct DummyType {};

template <typename C, typename T>
struct ExampleTemplate
{
    replace_type_t<C, DummyType, T> items;
};

и вызывать его через

int main()
{
    ExampleTemplate<std::vector<DummyType>, float> a;
    a.items.push_back(1.0);
    //a.items.push_back("Hello");  // prints an error message which shows that DummyType is replaced correctly

    ExampleTemplate<std::list<DummyType>, float> b;
    b.items.push_back(1.0);
    //b.items.push_back("Hello");  // prints an error message which shows that DummyType is replaced correctly

    ExampleTemplate<std::map<int, DummyType>, float> c;
    c.items[0]=1.0;
    //c.items[0]="Hello";          // prints an error message which shows that DummyType is replaced correctly
}

DEMO

Помимо не очень приятного синтаксиса, это имеет то преимущество, что

  1. Работает с любым количеством параметров шаблона по умолчанию- например, рассмотрим случай с std::map в примере.

  2. Нет необходимости явно указывать какие-либо параметры шаблона по умолчанию.

  3. Его можно легко расширить на более фиктивные параметры (тогда как пользователи, вероятно, не должны вызывать его ...).

Кстати: вместо фиктивного типа выможно также использовать std::placeholder ... только что понял, что это может быть немного лучше.

0 голосов
/ 14 марта 2011

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


/* Allows you to create template classes that allow users to specify only some
 * of the default parameters, and some not.
 *
 * Example:
 *  template <typename A = use_default, typename B = use_default>
 *  class foo
 *  {
 *              typedef use_default_param<A, int> a_type;
 *              typedef use_default_param<B, double> b_type;
 *              ...
 *  };
 *
 *  foo<use_default, bool> x;
 *  foo<char, use_default> y;
 */

struct use_default;

template<class param, class default_type>
struct default_param
{
        typedef param type;
};

template<class default_type>
struct default_param<use_default, default_type>
{
        typedef default_type type;
};

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...