шаблон , как параметр еще одного шаблона

zrab

   что-то туплю
   есть шаблон функции
    

template<template<class> class Cont, class U> typename
Cont<U>::iterator f(Cont<U>& d, const U& u){

как его применять? написал
 
 f<vector, int> 

, компилятор ругается.
 
Error 1 error C3207: 'f' : invalid template argument for 'Cont', class template expected


Помогите, под конец дня голова пухнет

Serab

у вектора еще есть параметры по умолчанию
vector — это не template <class> class

zrab

И воторой вопрос: у меня есть список. Можно в нем как-нибудь реализовать бинарный поиск? Если бы был итератор с произвольным доступом - то легко, но итератор-то двунаправленный (вроде)
Как тут быть — ХЗ.

Serab

нет, нельзя

zrab

Я и vector<int> пробовал - все равно ругается.

Serab

Посмотри определение типа vector

Serab

vector<int> - это тем более не template<class> class

zrab

Знаю, там их много. Но другие же даны по умолчанию (аллокатор и пр. так?

Serab

В общем посмотри определения стандартных адаптеров типа stack и queue и посмотри как они там это обошли.
Да, это неудобно, тоже когда-то с этим сталкивался.

zrab

точно, спасибо.

zrab

а, не обошли они.
просто-напросто использовали не классы-шаблоны в качестве параметров, а обыкновенные классы.

Serab

Да, я стандартной библиотекой давно не пользовался, у нас на работе своя.
Там действительно дублируется. Надо думать.

Serab

Ну все же раз в std так сделано, может и тебе так же поступить? Откажись от шаблонных шаблонных параметров и используй просто класс-контейнер. И инстанцируй vector<int>'ом.
Вот такие мысли.

deniska

Так подойдет?
template<template<class> class Cont, class U> typename 
Cont<U>::iterator f(Cont<U>& d, const U& u){
return d.begin;
}

template < class T >
class TVector : public vector < T > {
};

void Test {
TVector < int > v1;
int x = 5;
vector < int >::iterator it = f(v1, x);
}

Serab

Нет. По нескольким причинам.

deniska

Каким?

Dasar

Каким?
все использования класса vector придется заменить на использование своего класса TVector
а это скорее всего будет невозможно даже при мало-мальском использовании чужого (или ранее написанного) кода

deniska

Я просто вопрос понял "как вызвать эту функцию, если у меня есть вектор". А не "как написать модную фунцию, которая работает от вектора". То есть если уже есть такая функция ровно с тем объявлением, что задано в первом посте и нужно вызвать ее от вектора (а не так, как используется в остальном коде) - то, как мне кажется, мой метод подойдет.
Если вопрос в том, как правильно написать функцию f - то да, мое решение явно не для этого.

deniska

Если вопрос про то, какой должен быть интерфейс у функции f, чтобы работало с STL контейнерами, то нужно, видимо, писать так:

template<template<class, class> class Cont, class U, class A> typename
Cont<U, A >::iterator f(Cont<U, A >& d, const U& u)
{
return d.begin;
}

void Test {
vector < int > v1;
int x = 5;
vector < int >::iterator it = f(v1, x);
}

Serab

Каким?
Первую привел , а именно: нельзя использовать с vector напрямую, хотя замысел автора, очевидно, и состоял в том, чтобы использовать с любым контейнером. Таким образом приведенный код — это НЕ решение задачи.
Вторая причина более спорная на первый взгляд — добавление пустого производного класса, чтобы сделать workaround. Очень неоднозначное решение. Вообще наследование так использовать не стоит.

Serab

Я просто вопрос понял "как вызвать эту функцию, если у меня есть вектор". А не "как написать модную фунцию, которая работает от вектора".
Ну и как ее использовать-то, если есть вектор? Если мы не меняем функцию, тогда надо юзать reinterpret_cast (ну или обмануть себя и написать-таки static_cast). В общем к сожалению это не решение. Хотя можно добавить в TVector конструктор от vector< int >, но это чревато проблемами. Лучше тогда уж написать в стиле адаптера: указатель на vector< int > как данное-член.

Serab

Это уже лучше, так перегрузить исходный шаблон из первого поста можно, это будет работать, если типы вектора и второго параметра совпадают.
Минусы:
  непереносимо, строго говоря: мы полагаемся на конкретную реализацию шаблона vector (стандарт говорит лишь о том, что можно инстанцировать vector< T > при определенных условиях на тип T, но ничего не говорит о том, как это сделано: есть ли там еще дополнительные параметры и об их количестве).
  второй, опять же, более сомнительный (многим и первый покажется очень сомнительным но это неоправданное усложнение функции, подстраивание ее под определенное описание контейнера: шаблонный класс с двумя параметрами-типами, причем второй тип передается без изменений.
Все же предлагаю свое решение, как наиболее общее и лишенное перечисленных недостатков: модифицировать определение f следующим образом:

template<class Cont, class U> typename
Cont::iterator f(Cont& d, const U& u)
{
return d.begin;
}

void Test {
vector < int > v1;
int x = 5;
vector < int >::iterator it = f(v1, x);
}

Плюс/Минус: позволяет вызвать f, причем вторым параметром передать не строго тип, на который настроен шаблон, а все, что синтаксически корректно при использовании соответствующей функции.
Плюс в том, что по идеологии везде, где принимается ссылка/указатель на базовый класс, позволить принимать ссылку/указатель на производный. В этом случае
struct A {};
struct B : A {};

void Test {
vector < A* > v1;
B* x = new B;
vector < A* >::iterator it = f(v1, &x);
}
сработает с моим определением, но не сработает с твоим.
Что выбирать — решать автору.
Как дополнительный аргумент в пользу этого подхода приведу (повторно) следующий: в std шаблонные классы-адаптеры сделаны именно так.

Serab

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

deniska

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

template<class Cont> typename
Cont::iterator f(Cont& d, const typename Cont::value_type& u){
return d.begin;
}

void Test {
vector < int > v1;
int x = 5;
vector < int >::iterator it = f(v1, x);
}

deniska

Ну и как ее использовать-то, если есть вектор? Если мы не меняем функцию, тогда надо юзать reinterpret_cast (ну или обмануть себя и написать-таки static_cast).
Я это и имел ввиду, просто не тот кусок скопировал (только сейчас заметил). В момент вызова функции вместо vector подставить другой тип, у которого будет тот же самый адрес this. Это, конечно, жутко неправильно, но как временное решение может подойти.

Serab

Приятного аппетита.
Да, это круто. Если это написать вместо моего кода, то слова «Плюс/минус» можно вполне заменить на «плюс».
Сомнительна лишь необходимость определения типа/typedef'а value_type внутри всех контейнеров, используемых с этой функцией.

Serab

Вообще предлагаю тут подумать и понять, почему C++ не позволяет использовать шаблоны с параметрами по умолчанию в качестве аргументов-шаблонных параметров с отличным от полного количеством параметров. Надеюсь, ясно, что имел в виду: то, что в первом посте: почему нельзя подставить vector в то, что написал топикстартер.
Ответ подразумевается в виде: если бы это позволили, то вышел бы такой-то такой-то пиздец.

deniska

[offtop] Да я в смысле уже пообедал, как раз это время не был на форуме и ответил позднее [/offtop]
Вообще, как я понимаю, value_type так или иначе определяют во всех контейнерах. Если работают со своими - то он там может называться по другому, но тогда, как я понимаю, с помощью полиси компании запрещают пользоваться контейнерами из STL. Так что, скорее всего, этот typedef есть.
Твое решение мне тоже нравится. Просто мой вариант чуть отличается и решил его тоже запостить.

Serab

Да, это верно. В нашей компании, например, есть контейнеры с различным числом шаблонных параметров, в этом случае необходимо одно из последних двух решений здесь, все что выше — хуже.
По поводу value_type у меня просто был загон в сторону нешаблонных контейнеров.

deniska

По поводу value_type у меня просто был загон в сторону нешаблонных контейнеров.
Дык вроде там тоже можно определить этот typedef. Ничего плохого в тех контейнерах от наличия такого typedef не случится, зато добавит гибкости когда их используют шаблонные функции.
PS
На твой основной вопрос (почему плохо работают параметры по умолчанию в шаблонах) я потом (дома) отвечу. Это ближе к утру по московскому времени будет.

deniska

Вообще предлагаю тут подумать и понять, почему C++ не позволяет использовать шаблоны с параметрами по умолчанию в качестве аргументов-шаблонных параметров с отличным от полного количеством параметров. Надеюсь, ясно, что имел в виду: то, что в первом посте: почему нельзя подставить vector в то, что написал топикстартер.
Ответ подразумевается в виде: если бы это позволили, то вышел бы такой-то такой-то пиздец.
На самом деле совсем пиздец не будет, насколько я понимаю. Просто компиляторам придется повозиться и не всегда они смогут в итоге решить, что правильно. Причем, если верить Дэвиду Вандевурду, некоторые компиляторы (как расширение) позволяют это сделать, хоть это и не стандарт.
Вообще эта проблема очень близка к проблеме, почему нельзя делать typedef шаблоны. Если бы их можно было делать, то проблема уже обходится легко, например в данном случае можно сделать так:

template < typename T >
typedef vector < T > TVector;

...

f<TVector, int>(v1, x);

Последнее удобно было бы не только в этих примерах. Как понимаю typedef-шаблоны обсуждали в комитете по стандартизации, но вот с ними уже есть подводные камни. Например, вот этот:

void F(long);

template < typename T > typedef T DT;

template < typename T > void F(DT<T>);

int main {
F(42);
}

Serab

Я туплю чего-то. А какая там проблема с этим кодом?

deniska

Какую из двух функций F вызвать. Для первой нужно преобразовать int в long, а вторая подходит "идеально", если не считать, что там шаблон. Какую бы выбрал ты?
Оставить комментарий
Имя или ник:
Комментарий: