template и friend operator<<

 
0
 
C++
ava
cupper | 05.11.2010, 18:01
Хачу сделать дружественную функцию оператор << для шаблонного класса. Уже часа два бороздю интернет, листаю книжки форумы... понял что шаблонные функции друзья для классов это уже сложно, а чтобы найти гдето рабочий кусок того что мне нужно так это вообще нету нигде.
Вот написал следующее

template<typename T>
class test2{
    friend template<typename U> ostream& operator<< <>(ostream& os, const test2<U>&);
public:
    test2(T i): a(i){};
private:
    T a;
};

template<typename U>
ostream& operator<<(ostream& os, const test2<U>& val){
    os<<val.a<<endl;
    return os;
}
...
test2<int> b(15);
cout<<b;

Написано не от балды, в основе взята книга "Новые сложные задачи на С++" + пару похожих тем на разных формах. Но данная реализация не работает и при компиляции выводит

main.cpp:25:9: error: expected unqualified-id before ‘template’
main.cpp: In function ‘std::ostream& operator<<(std::ostream&, const test2<U>&) [with U = int, std::ostream = std::basic_ostream<char>]’:
main.cpp:48:8:   instantiated from here
main.cpp:29:4: error: ‘int test2<int>::a’ is private
main.cpp:34:2: error: within this context

Мои идеи и знания в этой области иссякли, нужна "помощь зала".


gcc version 4.5.1 (Ubuntu/Linaro 4.5.1-7ubuntu2) 
Ответы (17)
ava
null56 | 05.11.2010, 18:30 #

#include <iostream>

template<typename T>
class test2
{
    template <typename U>
    friend std :: ostream & operator<< (std :: ostream& os, const test2<U> & );
    
public:
    test2(T i): a(i)
    {
    }
    
private:
    T a;
};

template<typename U>
std :: ostream& operator<<(std :: ostream& os, const test2<U>& val)
{
    os<<val.a<< std :: endl;
    return os;
}

int main()
{
test2<int> b(15);
std :: cout<<b;
return 0;
ava
cupper | 05.11.2010, 19:45 #
жуть smile перепробовал различные вариации но в эту видимо не попал :)

Теперь по поводу переносимости. Все в той же книжке было написано что вариант без <> плохой т.к. не везде поддерживается, это вообще в наше время актуально ? или современные версии компиляторов устоялись в этом синтаксисе ?
ava
cupper | 05.11.2010, 21:55 #
избавился от этой проблемы сразу получил другие.
Есть у меня следующие файлы

dynArray.hpp
----------------
#include<algorithm>
#include<ostream>
...

template<typename T>
class DynArray{
    template<typename S>
    friend std::ostream& operator<<(std::ostream&, const DynArray<S>&);
...
private:
    size_t size_;
    T* data_;
};

template<typename S>
std::ostream& operator<<(std::ostream& os, const DynArray<S>& mas){
    std::for_each(mas.data_, mas.data_ + mas.size_, [&os](const S& i)->void{os<<i<<" ";});
    return os;
}


main.cpp
-----------
#include<iostream>
#include<vector>
#include<algorithm>
#include <fstream>
#include "data.hpp"

using namespace std;

int main(){
    DynArray<int> mas(10);
    for(int *i = mas.begin(); i != mas.end(); i++)
        *i = 3.14 + *(i-1);
    cout<<mas<<endl;
    
}

Оно все прекрасно работает.
Далее есть следущий класс

data.hpp
-----------
#include"dynarray.hpp"
#include<string>

class Data{
    friend std::ostream& operator<<(std::ostream&, const Data&);
...
public:
...
    typedef std::vector<std::pair<std::string, DynArray<int> > > vecPair;
private:
    vecPair data_;
};


data.cpp
----------
...
std::ostream& operator<<(std::ostream& os, const Data& data){
    for(int i=0; i<data.data_.size(); i++)
    {
        os<<"[size: "<<(data.data_[i]).first<<"] "<<(data.data_[i]).second<<std::endl;
    }
              return os;
}

И тут я получаю следующие ошибки

dynarray.hpp: In function ‘std::ostream& operator<<(std::ostream&, const DynArray<T>&) [with S = int, std::ostream = std::basic_ostream<char>]’:
data.cpp:41:63:   instantiated from here
dynarray.hpp:132:2: error: ‘for_each’ was not declared in this scope

Притом если в фале data.cpp в реализации оператора << убрать задействование шаблона

os<<"[size: "<<(data.data_[i]).first<<"] "<<std::endl;

то оно собирается без ошибок... Че за чертовщина ?

И опять таки еще раз про реализацию << в data.cpp
если ее сделать вот так

std::ostream& operator<<(std::ostream& os, const Data& data){
    for_each(data.data_.begin(), data.data_.end(),
        [&os](std::pair<std::string, DynArray<int> >& item)->void{
            os<<"[size: "<<item.first<<"] "<<item.second<<std::endl;
        });
    return os;
}

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

data.cpp:46:4: error: no matching function for call to ‘for_each(std::vector<std::pair<std::basic_string<char>, DynArray<int> > >::const_iterator,
std::vector<std::pair<std::basic_string<char>, DynArray<int> > >::const_iterator, operator<<(std::ostream&, const Data&)::
<lambda(std::pair<std::basic_string<char>, DynArray<int> >&)>)’
In file included from data.hpp:4:0,
                 from data.cpp:2:
dynarray.hpp: In function ‘std::ostream& operator<<(std::ostream&, const DynArray<T>&) [with S = int, std::ostream = std::basic_ostream<char>]’:
data.cpp:45:42:   instantiated from here
dynarray.hpp:132:2: error: ‘for_each’ was not declared in this scope


где моя ошибка ??? чего я упустил ?(
ava
rudvil | 05.11.2010, 21:58 #
Нужно добавить std:: к for_each
std::for_each
Компилятор пишет что не может найти этого имени в текущем пространстве имен.
ava
cupper | 05.11.2010, 22:29 #
Упустил этот момент. Вернее даже не подумал о нем, потому что если без data.cpp то оно все работало, а оказалось что для data.cpp  оно требуется

Ну это помогло избавиться от первой ошибки, и она мигом превратила ко второй smile Теперь он на оба for_each пишет

data.cpp:46:4: error: no matching function for call to ‘for_each(std::vector<std::pair<std::basic_string<char>, DynArray<int> > >::const_iterator,
std::vector<std::pair<std::basic_string<char>, DynArray<int> > >::const_iterator, operator<<(std::ostream&, const Data&)::<lambda
(std::pair<std::basic_string<char>, DynArray<int> >&)>)’
In file included from data.hpp:4:0,
                 from data.cpp:2:
dynarray.hpp: In function ‘std::ostream& operator<<(std::ostream&, const DynArray<T>&) [with S = int, std::ostream = std::basic_ostream<char>]’:
data.cpp:45:42:   instantiated from here
dynarray.hpp:132:2: error: no matching function for call to ‘for_each(int* const&, int*, operator<<(std::ostream&, const DynArray<T>&) [with S = int,
std::ostream = std::basic_ostream<char>]::<lambda(const int&)>)’
 
ava
null56 | 06.11.2010, 03:23 #
вот с лямбдой у меня проверить не получиться, так что предположу...
ругается на то, что нет такого прототипа функции for_each.
for_each имеет следующий прототип

template<class InputIterator, class Function>
  Function for_each(InputIterator first, InputIterator last, Function f)
  {
    for ( ; first!=last; ++first ) f(*first);
    return f;
  }

как видишь типы первых двух аргументов должны совпадать

Предположение

dynarray.hpp:132:2: error: no matching function for call to ‘for_each(int* const&, int*, operator<<(std::ostream&, const DynArray<T>&) [with S = int,
std::ostream = std::basic_ostream<char>]::<lambda(const int&)>)’

это видимо про эту строчку

for_each(mas.data_, mas.data_ + mas.size_, [&os](const S& i)->void{os<<i<<" ";});

на счет лямбда не знаю, а вот типы первых двух аргументов не равны
при при data_ == int, ты передаешь первым "int *", а вторым "int * + unsigned int", попробуй измени этот момент
ava
boostcoder | 06.11.2010, 03:45 #
Цитата (cupper @  5.11.2010,  21:55 findReferencedText)
[&os](std::pair<std::string, DynArray<int> >& item)->void{

зачем так сложно?
вот:

[&os](const vecPair::value_type& item)->void{

 smile 

и:
Цитата


->void


тут лишнее.
ava
boostcoder | 06.11.2010, 04:20 #

#include <iostream>
#include <vector>
#include <string>
#include <utility>
#include <algorithm>

template<typename T>
struct DynArray {
   T data_;
};

typedef std::vector<std::pair<std::string, DynArray<int> > > vecPair;

std::ostream& operator<<(std::ostream& os, const vecPair& data){
    std::for_each(data.begin(), data.end(),
        [&os](const vecPair::value_type& item)->void{
            os << item.first << item.second.data_ << std::endl;
        }
    );
    return os;
}
int main() {
   vecPair vec;
   std::cout << vec;
}

http://liveworkspace.org/code/fe41f1aeca74...0fa9337091f696a
ava
null56 | 06.11.2010, 05:04 #
boostcoder, слушай,а вторая ошибка

data.cpp:46:4: error: no matching function for call to ‘for_each(std::vector<std::pair<std::basic_string<char>, DynArray<int> > >::const_iterator,
std::vector<std::pair<std::basic_string<char>, DynArray<int> > >::const_iterator, operator<<(std::ostream&, const Data&)::<lambda
(std::pair<std::basic_string<char>, DynArray<int> >&)>)’


связана с тем, что

for_each(data.data_.begin(), data.data_.end(),
        [&os](std::pair<std::string, DynArray<int> >& item)->void{
            os<<"[size: "<<item.first<<"] "<<item.second<<std::endl;
        });

компилятор приводил метод

vector::begin

public member type

      iterator begin ();
      const_iterator begin () const;

к константному итератору и при вызове в for_each оператора *, возвращался тип "const T &", что требовало в лямбде так же мобификатора const?
ava
boostcoder | 06.11.2010, 07:00 #
Цитата (null56 @  6.11.2010,  05:04 findReferencedText)
к константному итератору и при вызове в for_each оператора *, возвращался тип "const T &", что требовало в лямбде так же мобификатора const?

так он в лямбду передавал неконстантную ссылку, хотя ссылка на контейнер константная.
Цитата


[&os](std::pair<std::string, DynArray<int> >& item)->void{

ava
null56 | 06.11.2010, 13:12 #
boostcoder, да, я тоже обратил на это внимание, но проверить предположение номер 2 негде было ) сейчас собрал gcc-4.5.1
ava
cupper | 06.11.2010, 18:11 #
Что то я непонял всей глубины выших мыслей про константы...
Посмотрите на пост где я приводил код, обратите внимание на связку dynArray.hpp и main.cpp. Оно работает, т.е. оно собирается запускается и выводить на экран то что ожидалось.

Теперь вы добавляем в проект файлы data.hpp b data.cpp с их перегрузкой оператора <<

std::ostream& operator<<(std::ostream& os, const Data& data){
    std::for_each(beg, end,
        [&os](std::pair<std::string, DynArray<int> >& item)->void{
            os<<"[size: "<<item.first<<"] "<<item.second<<std::endl;
        });
              return os;
}

Все такое самое но оно уже не работает, и мало того что ругается на for_each который в для Data, так он начинает точно также ругаться и на << который для DynArray без Data прекрасно работает !!!
Предположим что я не вижу какойто проблеммы с константами именно в этом методе, сделаем так

std::ostream& operator<<(std::ostream& os, Data& data){
    std::vector<std::pair<std::string, DynArray<int> > >::iterator beg = data.data_.begin();
    std::vector<std::pair<std::string, DynArray<int> > >::iterator end = data.data_.end();
    std::for_each(beg, end,
        [&os](std::pair<std::string, DynArray<int> >& item)->void{
            os<<"[size: "<<item.first<<"] "<<item.second<<std::endl;
        });
    return os;
}

Всее !!! нету констант больше вообще. И что я получаю в ошибках

data.cpp:45:4: error: no matching function for call to ‘for_each(std::vector<std::pair<std::basic_string<char>, DynArray<int> > >::iterator&,
std::vector<std::pair<std::basic_string<char>, DynArray<int> > >::iterator&, operator<<(std::ostream&, Data&)::<lambda
(std::pair<std::basic_string<char>, DynArray<int> >&)>)’
In file included from data.hpp:4:0,
                 from data.cpp:3:
dynarray.hpp: In function ‘std::ostream& operator<<(std::ostream&, const DynArray<T>&) [with S = int, std::ostream = std::basic_ostream<char>]’:
data.cpp:44:42:   instantiated from here
dynarray.hpp:132:2: error: no matching function for call to ‘for_each(int* const&, int*, operator<<(std::ostream&, const DynArray<T>&) [with S = int,
std::ostream = std::basic_ostream<char>]::<lambda(const int&)>)’

И что же получается ? он говорит что нет метода вот с такой сигнатурой for_each(iterator&, iterator&, ...)  что то я в этом сомневаюсь :(

Прикрепляю архив, там исходники эти и Makefile, для собрки нужно gcc 4.5.1 и boost.
Запускаем makefile убеждаемся что оно работает, лезем в файл data.cpp в самый низ, там проблематичный оператор << разкомментируем его и смотрим на ошибки.
Вырванные куски кода из контекста плохо обрисовывают картину.
ava
boostcoder | 06.11.2010, 18:29 #
Цитата (cupper @  6.11.2010,  18:11 findReferencedText)
Прикрепляю архив

какое-то странное название архива.. сервер небось какой :crazy

Добавлено позднее:
первое что бросилось в глаза, это Makefile.
должен быть таким:

CXX    = g++
LD      = g++
LDFLAGS += -lboost_system -lboost_serialization
CXXFLAGS += -std=c++0x
asio: data.o main.o
    $(LD) $(LDFLAGS) data.o main.o -o asio
data.o: data.cpp data.hpp
    $(CXX) $(CXXFLAGS) -c data.cpp
main.o: main.cpp data.hpp
    $(CXX) $(CXXFLAGS) -c main.cpp
clean:
    rm -f asio data.o main.o


далее...
ava
boostcoder | 06.11.2010, 18:45 #
Цитата (cupper @  6.11.2010,  18:11 findReferencedText)
Запускаем makefile убеждаемся что оно работает, лезем в файл data.cpp в самый низ, там проблематичный оператор << разкомментируем его и смотрим на ошибки. 

раскомментировал. и что, где ошибки?

при запуске выводит это:

$ ./asio
78 82 87 91 96 100 105 109 114 118
52 55 58 61 64 67 70 73 76 79

ava
cupper | 06.11.2010, 19:36 #
эх :( обидно то как, столько времени я потратил на эту ошибку, столько нервов, людей напряг, а дело то все было совсем не в коде, а makefile пропустил я чертовы эти параметры
data.o: data.cpp data.hpp
    $(CC) -c data.cpp
а он мне ведь зеленым по черному писал что мол варнинги, что мол нужно чтобы было указано -std=c++0x :(( а я прицепился к error for_each

эх, извините люди за ваше можно сказать в пустую потраченное время.
ava
boostcoder | 06.11.2010, 19:42 #
Цитата (cupper @ 6.11.2010,  19:36)
эх :( обидно то как, столько времени я потратил на эту ошибку, столько нервов, людей напряг, а дело то все было совсем не в коде, а makefile пропустил я чертовы эти параметры

data.o: data.cpp data.hpp
     $(CC) -c data.cpp

а он мне ведь зеленым по черному писал что мол варнинги, что мол нужно чтобы было указано -std=c++0x :(( а я прицепился к error for_each



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

:girl_crazy

Добавлено позднее:
и перестань народ пугать подобными строками:
Цитата (cupper @  6.11.2010,  18:11 findReferencedText)
std::pair<std::string, DynArray<int> >

Цитата (cupper @  6.11.2010,  18:11 findReferencedText)
std::vector<std::pair<std::string, DynArray<int> > >::iterator

typedef`ы ведь на что? ;)
ava
cupper | 06.11.2010, 21:49 #
typedef на столько же добро, на сколько и зло.
отдельное спасибо за Mikefile, тот который был у меня это самый мой первый написанный мною smile теперь буду основывать на тот который вы сделали smile
Зарегистрируйтесь или войдите, чтобы написать.
Фирма дня
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Участники
advanced
Отправить