#include <memory>
#include <vector>
class Shape
{
public:
virtual void
draw() = 0;
};
class Box : public Shape
{
virtual void draw() {}
};
class Circle : public Shape
{
virtual void draw() {}
};
int
main()
{
std::vector<std::unique_ptr<Shape>> v;
v.push_back(std::make_unique<Box>());
v.push_back(std::make_unique<Circle>());
for (auto i : v) {
i->draw();
}
return 0;
}
i->draw();
}
return 0;
}
--
Antes de enviar um e-mail para o grupo leia:
http://www.ccppbrasil.org/wiki/Lista:AntesdePerguntar
--~--~---------~--~----~------------------------------
[&] C & C++ Brasil - http://www.ccppbrasil.org/
Para sair dessa lista, envie um e-mail para ccppbrasil-...@googlegroups.com
Para mais opções, visite http://groups.google.com/group/ccppbrasil
--~--~---------~--~----~--~-~--~---~----~------------
Tive um professor de fisica, nos tempos de escola, que costumava dizer que "dificil nao existe, dificil é o desconhecido..."
--
Acho que a premissa está errada. For-loop é uma coisa, unique_ptr é outra, unique_ptr está totalmente atreladoà move-semantics, ambos inexpressíveis em CO move está presente no C sem que haja um conceito novo na linguagem. No container vector pro exemplo do C que estou fazendo, posso ter um vector de ponteiros e vou ter lá um move_back(& p ) significando que o ownership vai ser passado adiante. Mas o interessante é que os conceitos da linguagem não precisam mudar.
On Dec 11, 2014, at 16:14, Thiago Adams <thiago...@gmail.com> wrote:Acho que a premissa está errada. For-loop é uma coisa, unique_ptr é outra, unique_ptr está totalmente atreladoà move-semantics, ambos inexpressíveis em CO move está presente no C sem que haja um conceito novo na linguagem. No container vector pro exemplo do C que estou fazendo, posso ter um vector de ponteiros e vou ter lá um move_back(& p ) significando que o ownership vai ser passado adiante. Mas o interessante é que os conceitos da linguagem não precisam mudar.Inexpressíveis da forma que são suportados em c++. É expressível, assim como "saudade" pode ser representado por "missing someone”e padrões GoF em uma linguagem não tem implementação necessária em outras. De qualquer forma, se o alvo é move semantics,o conceito terá que ser apreendido, seja em C++, Rust, ou C + STL rip-off.
On Dec 11, 2014, at 16:38, Francisco Lopes <obl...@gmail.com> wrote:On Dec 11, 2014, at 16:14, Thiago Adams <thiago...@gmail.com> wrote:Acho que a premissa está errada. For-loop é uma coisa, unique_ptr é outra, unique_ptr está totalmente atreladoà move-semantics, ambos inexpressíveis em CO move está presente no C sem que haja um conceito novo na linguagem. No container vector pro exemplo do C que estou fazendo, posso ter um vector de ponteiros e vou ter lá um move_back(& p ) significando que o ownership vai ser passado adiante. Mas o interessante é que os conceitos da linguagem não precisam mudar.Inexpressíveis da forma que são suportados em c++. É expressível, assim como "saudade" pode ser representado por "missing someone”e padrões GoF em uma linguagem não tem implementação necessária em outras. De qualquer forma, se o alvo é move semantics,o conceito terá que ser apreendido, seja em C++, Rust, ou C + STL rip-off.Dois pontos que acho interessante são:- Dificuldade em corromper a proposta de ownership durante o uso. Neste ponto parece que Rust > C++ > C + C-STL.- Facilidade no uso. Sem opinião formada.
Se me permitem atear fogo agora....
Eu acho que a dificuldade de se aprender C++ só reflete a dificuldade de se criar softwares complexos. Tínhamos que ter um CREA/CRM/OAP sim... e tínhamos que exigir que TODOS profissionais tivessem conceitos de ownership, thread-safety, ponteiros, cache-locality, round-trips e etc.... Não precisa usar sempre. Mas sabendo o que são, pode-se ter uma discussão sensata sobre que ferramenta usar e quando.
Ao invés de se criar "agora sim, uma linguagem fácil de programar que todos vão adorar e vc cria seu sistema na cloud com duas linhas e três cliques..."; deveriamos exigir que TODOS soubessem C, C++, SQL, leitura de ASM e uma, SÓ UMA, linguagem mais dinâmica média.. (Python?) e outra beeem leve (JS? Lua).... enfim, ter só um número limitado de linguagens 'aprovadas' pelo CREA...etc...
Resolver preguiça de programador de fazer/aprender direito com uma linguagem/framework/ferramenta nova é resolver o problema errado...
C++ é dificíl de aprender? OK, as inscrições p/ o curso de moda são naquela fila, ó....
C++ é complexo demais p/ fazer um sisteminha de cadastro na web?? Sim! Óbvio... faça em Python... não invente uma nova linguagem para fazer essa merda que python já faz fácil o suficiente...
ps: Python/Perl/Ruby/tantufas... escolha UMA.
Algo que sempre me trouxe conforto no c++ era pensar que no pior caso escrevo c e compilo no c++.ou seja para que c?
Agora fico um pouco preocupado com o abandono do c pela ms por ex e acho que o padrão. C++ deveria mais conectado ao c.
On Dec 11, 2014, at 22:14, Thiago R Adams <thiago...@gmail.com> wrote:Algo que sempre me trouxe conforto no c++ era pensar que no pior caso escrevo c e compilo no c++.ou seja para que c?
Agora fico um pouco preocupado com o abandono do c pela ms por ex e acho que o padrão. C++ deveria mais conectado ao c.Acho que os comitês não se batem em personalidade. A vantagem dos padrões separados é que o tempo não pára, e C tambémevolui, a passos e mudanças menos drásticas que C++, mas segue mesmo assim. Vai saber o que C vai se tornar, ou pára no tempoe provavelmente se torna obsoleto (ou pelo menos com a maioria pensando "por que não morre logo”) ou evolui adquirindo featuresinteressantes e um ponto de vista diferente do C++. O quê a MSFT adota ou não é apenas um ponto de vista de conveniência, nãoalgo que realmente importe, o tema transcende o nascimento e a morte da MSFT. É uma pena para os desenvolvedores Windows, masque estes saibam que há outros sistemas, outros compiladores, etc.
Bixo, no grupo de .Net o cara não sabe o que é Ajax.
Eu sempre defendo C++ e vou continuar defendendo.No entanto, uma das coisas que não nego é que é uma linguagem difícil de aprender e cheia de detalhes.Vou colocar um exemplo aqui que não compila, eu sei o motivo, mas vou colocar apenas como exemplo das dificuldades que um iniciante passa.#include <memory>
#include <vector>class Shape
{
public:
virtual void draw() = 0;
};class Box : public Shape
{
virtual void draw() {}
};class Circle : public Shape
{
virtual void draw() {}
};
int main()
{std::vector<std::unique_ptr<Shape>> v;
v.push_back(std::make_unique<Box>());
v.push_back(std::make_unique<Circle>());for (auto i : v) {
i->draw();
}
return 0;
}
Acho interessante estas discussões sobre o ensino no brasil.
Quando se estuda algo, claro que o assunto se torna mais simples.
Mas o que me preocupa é a complexidade do C++ independente da questão se alguém
estudou ou não. Mesmo aquela questão
inicial, se for discutida por experts vai render muita polêmica.
Existe uma diferença entre “fácil de entender e difícil de fazer” e “difícil de entender e fácil de fazer”.
Por exemplo, é fácil de entender que para tocar piano basta apertar as teclas. Porém tocar uma música é difícil. O C me parece que segue esta linha já que possui um número pequeno de conceitos. O livro “The C Programming Language” tem 228 páginas apenas.
Já o C++, é fácil de fazer (quando não dá erro de templates) e difícil de entender. A partir do C++11, o C++ se tornou mais fácil de usar e mais difícil de entender. Para quem já sabia C++03, era uma questão de aprender a parte nova (que é o meu caso). Já para quem está iniciando, não consigo nem imaginar como aos poucos aquele “fácil de fazer” vai ser entendido. E diferentemente das linguagens mais alto nível, se você não souber o que está fazendo em C++ mais cedo ou mais tarde vai ter problemas. Então uma das dificuldades de se ensinar C++ atualmente parece esta, a tal ponto que é melhor dizer “faz assim por enquanto que um dia você vai entender”.
Existe uma linha, que inclusive o Stroustrup defende, que pare ensinar C++ não é preciso ensinar C antes.
Eu penso diferente. Ensina-se C e qualquer dúvida sobre como as coisas funcionam, basta recorrer aquela mais dúzia de conceitos do C. Ao meu ver também o padrão C++ deveria ser fiel ao C deixando o C como uma camada mais baixo nível do C++.
Mudando um pouco a questão: as pessoas tem se interessado em aprender C++?C++ é uma linguagem complexa para dominar mas isso não é problema para quem está motivado. Mas C++ tem motivado?A minha impressão (reddit) é que apenas o fim motiva, exemplo: quero programar jogos e C++ ainda é a melhor opção pra isso, então vamos lá...fazer o que. Mas para quem tem opções, C++ não está interessando.Aí o sujeito cai num trabalho de manutenção de um legado em C++ escrito há 15 anos, usando paradigma de sorvete napolitano: 1/3 OO, 1/3 estruturado, 1/3 do cara que leu Alexandrescu mas não entendeu muito bem.

On Tuesday, December 23, 2014 11:01:59 AM UTC-2, Thiago Adams wrote:
Outro ponto em relação a complexidade do C++ é que pode causar o efeito psicológico de alguém que sabe bem C++ achar que com isso vai estar fazendo um código melhor, somente pelo fato de estar usando templates e coisas modernas ou ter cuidado dezenas de regrinhas na elaboração da classe."Meu código é bom pois é moderno. Preciso de um compilador C++14" "Tudo aqui é template, super genérico."O fato de ser moderno não implica necessariamente ser melhor.
On Dec 23, 2014, at 16:34, José <jrzi...@gmail.com> wrote:Mudando um pouco a questão: as pessoas tem se interessado em aprender C++?C++ é uma linguagem complexa para dominar mas isso não é problema para quem está motivado. Mas C++ tem motivado?A minha impressão (reddit) é que apenas o fim motiva, exemplo: quero programar jogos e C++ ainda é a melhor opção pra isso, então vamos lá...fazer o que. Mas para quem tem opções, C++ não está interessando.
On Dec 23, 2014, at 16:56, Francisco Lopes <obl...@gmail.com> wrote:On Dec 23, 2014, at 16:34, José <jrzi...@gmail.com> wrote:Mudando um pouco a questão: as pessoas tem se interessado em aprender C++?C++ é uma linguagem complexa para dominar mas isso não é problema para quem está motivado. Mas C++ tem motivado?A minha impressão (reddit) é que apenas o fim motiva, exemplo: quero programar jogos e C++ ainda é a melhor opção pra isso, então vamos lá...fazer o que. Mas para quem tem opções, C++ não está interessando.O julgamento disto é bem controverso, veja por exemplo no GitHub com relação ao interesse nos padrões novosde linguagens veteranas:

O fix correto seria:
for(auto &i : v)
Nao existe a possibilidade de haver "move" nesse caso particular, e se houvesse (quando ao inves de usar auto && se usasse o tipo unique_ptr<Shape> &&) seria um erro logico que levaria os dois elementos do vetor a abrigar dois nullptr's(internos aos unique_ptr), tornando qualquer tentativa, posterior ao for, de acesso aos dados-membros (ou funcoes virtuais) de objetos apontados por tais enderecos a um erro de dereferencia de ponteiros nullptr.
E os "virtual"'s em Box e Circle adicionam espaco de memoria desnecessariamente para os respectivos tipos, portanto deveriam ser removidos.
E quanto ao conteudo central da discussao, eu discordo fortemente. Nenhum iniciante nivel "for, while, if" vai usar unique_ptr. E na medida em que o estudante aprende o problema de lidar com vazamento de memoria, ele naturalmente vai reconhecer o sentido do unique_ptr e compreender que ele nao deve ser copiado pois isso levaria a um destruction+delete de uma area de memoria que ja foi destruida e deletada anteriormente (o primeiro destruction+delete iria funcionar, o segundo iria falhar), e que se ele quiser que o unique_ptr seja inteligentemente automatico para corrigir isso ele entendera que isso custara um contador inteiro adicional e que deve usar shared_ptr.
Dizer que o C++ deveria ser mais facil quando a tal complexidade nao e' nada mais do que uma descricao de uma questao fundamental e' tao absurdo quanto dizer que um livro de circuitos analogicos nao deve conter matematica diferencial. Se voce quer resolver um problema complexo voce precisa de ferramentas que representam aquela complexidade, excluir a ferramenta nao resolve o problema, assim como remover matematica diferencial e integral pode ate ajudar os que nao querem aprender matematica diferencial e integral mas nao resolve a realidade dos problemas de circuitos analogicos.
On Thursday, December 11, 2014 at 3:19:49 PM UTC-2, Уθя¡ςκ wrote:
On Dec 11, 2014, at 13:56, Thiago Adams <thiago...@gmail.com> wrote:Eu sempre defendo C++ e vou continuar defendendo.No entanto, uma das coisas que não nego é que é uma linguagem difícil de aprender e cheia de detalhes.Vou colocar um exemplo aqui que não compila, eu sei o motivo, mas vou colocar apenas como exemplo das dificuldades que um iniciante passa.#include <memory>
#include <vector>class Shape
{
public:
virtual void draw() = 0;
};class Box : public Shape
{
virtual void draw() {}
};class Circle : public Shape
{
virtual void draw() {}
};
int main()
{std::vector<std::unique_ptr<Shape>> v;
v.push_back(std::make_unique<Box>());
v.push_back(std::make_unique<Circle>());for (auto i : v) {
short fix: for(auto &&i : v)IMO, o range loop que faz copia não funcionar é bom sinal não é? Mesmo para iniciantes. Em qualquer linguagem(é o tipo de coisa que acontece em Rust também), se ele quer usar unique-ownership, ele tem que arcar com issoe entender as conseqüências.Uma proposta de syntatic-sugar que vem ao caso é N3853: Range-Based For-Loops: The Next Generation—Stephan T. Lavavej,onde for (i : v) já implica auto && pra i.
Para sair dessa lista, envie um e-mail para ccppbrasil-...@googlegroups.comi->draw();
}
return 0;
}
--
Antes de enviar um e-mail para o grupo leia:
http://www.ccppbrasil.org/wiki/Lista:AntesdePerguntar
--~--~---------~--~----~------------------------------
[&] C & C++ Brasil - http://www.ccppbrasil.org/
Para mais opções, visite http://groups.google.com/group/ccppbrasil
--~--~---------~--~----~--~-~--~---~----~------------
--
Antes de enviar um e-mail para o grupo leia:
http://www.ccppbrasil.org/wiki/Lista:AntesdePerguntar
--~--~---------~--~----~------------------------------
[&] C & C++ Brasil - http://www.ccppbrasil.org/
Para sair dessa lista, envie um e-mail para ccppbrasil-unsubscribe@googlegroups.com
Para mais opções, visite http://groups.google.com/group/ccppbrasil
--~--~---------~--~----~--~-~--~---~----~------------
> Para sair dessa lista, envie um e-mail para ccppbrasil-unsubscribe@googlegroups.com
> Para mais opções, visite http://groups.google.com/group/ccppbrasil
> --~--~---------~--~----~--~-~--~---~----~------------
--
Antes de enviar um e-mail para o grupo leia:
http://www.ccppbrasil.org/wiki/Lista:AntesdePerguntar
--~--~---------~--~----~------------------------------
[&] C & C++ Brasil - http://www.ccppbrasil.org/
Para sair dessa lista, envie um e-mail para ccppbrasil-unsubscribe@googlegroups.com
Para mais opções, visite http://groups.google.com/group/ccppbrasil
--~--~---------~--~----~--~-~--~---~----~------------
for(unique_ptr<Shape> &&i : v)
on v é um lvalue é erro de compilação.
Tem razão, eu fiz o teste, nem mesmo compila:#include <memory>
#include <vector>
#include <iostream>
using namespace std;
class Shape
{
public:
virtual void draw() = 0;
};
class Box : public Shape
{
void draw() {cout << "box" << endl;}
};
class Circle : public Shape
{
void draw() {cout << "circle" << endl;}
};
int main()
{
std::vector<std::unique_ptr<Shape>> v;
v.push_back(std::make_unique<Box>());
v.push_back(std::make_unique<Circle>());
for (std::unique_ptr<Shape> &&i : v) {
cout << i.get() << endl;
i->draw();
}
return 0;
}
$ CC test250.cpp -std=c++14
test250.cpp:29:34: error: rvalue reference to type 'unique_ptr<[2 * ...]>' cannot bind to lvalue of type
'unique_ptr<[2 * ...]>'
for (std::unique_ptr<Shape> &&i : v) {
^ ~
/usr/include/c++/v1/vector:606:54: note: selected 'begin' function with iterator type 'iterator' (aka
'__wrap_iter<std::__1::unique_ptr<Shape, std::__1::default_delete<Shape> > *>')
_LIBCPP_INLINE_VISIBILITY iterator begin() _NOEXCEPT;
^
1 error generated.
Para sair dessa lista, envie um e-mail para ccppbrasil-unsubscribe@googlegroups.com
Esse post e' extenso, vou tentar fazer uma sintese, baseado em:
1- Na resposta do Leandro Melo e do Уθя¡ςκ
2- Neste artigo do Scott Meyers
3- Neste artigo do Thomas Becker
4- No final do capitulo 2 do livro do Scott Meyers "Effective Modern c++"
Esse post e' extenso, vou tentar fazer uma sintese, baseado em:
1- Na resposta do Leandro Melo e do Уθя¡ςκ
2- Neste artigo do Scott Meyers
3- Neste artigo do Thomas Becker
4- No final do capitulo 2 do livro do Scott Meyers "Effective Modern c++"
Vou separar cada um dos casos tentando explicar porque eles funcionam ou nao, por vezes usando uma nova proposta de tabela de colisoes de referencias.
No meio do post darei um exemplo de uma pequena implementacao de vector<bool> usando "proxy".
Ao final darei uma explicacao do porque que o codigo do Уθя¡ςκ funcionaria para o problema proposto por Leandro Melo baseado no ISO/IEC 14882:201.
Começo propondo uma nova...
Tabela de regra de colapso de referencias geral:
Na parte 8 do artigo de Thomas Becker ele fornece uma tabela de colisoes de referencia. A tabela que proponho a seguir e' geral:Meio de facilmente decorar a tabela
'A&' becomes A'A&' &becomesA&'A&' &&becomesA&'A' becomes A'A' & becomes A&'A' && becomes A&&
Do meio ate o final e' simples de decorar. Entao vou focar do inicio ate o meio.
Regra: sempre escolha o menor.
Entre nada e 1&, escolhemos o nada
Entre 1 & e 1 &, escolhemos 1 &
entre 2 & e 1 &, escolhemos 1 &
Caso 1: Codigo do Thiago Adams + auto puro
Os unique_ptr dentro do vector sao lvalue, portanto a primeira regra de colisao e' observada:'A&' becomes A
Entao, nesse caso, auto sera unique_ptr<Shape>.
Como unique_ptr nao pode ser copiado, entao o compilador emitira' um erro.
Caso 2: Codigo do Thiago Adams + auto &
Os unique_ptr dentro do vector sao lvalue, portanto a segunda regra de colisao e' observada:
'A&' & becomes A&
Entao, nesse caso, auto sera unique_ptr<Shape> &
Nao ha' copia, entao o compilador permitira isso normalmente.
Caso 3: Codigo do Thiago Adams + auto && (do Уθя¡ςκ)
Os unique_ptr dentro do vector sao lvalue, portanto a terceira regra de colisao e' observada:
'A&' && becomes A&
Entao, nesse caso, auto sera unique_ptr<Shape> &
Nao ha' copia, entao o compilador permitira isso normalmente.
Sobre vector<bool>
Scott Meyers explica em seu livro EMC++ uma coisa chamada proxy class, vou chamar aqui de "classe representante".
O vector<bool> e' diferente dos outros vectors. Um programador menos experiente se surpreenderia (como eu me surpreendi ao ler o Leandro Melo) ao saber que vector<bool> nao aloca dinamicamente e internamente uma serie de bools, como faz com outros tipos. Ao inves disso ele trata os seus bools como bits.
Vamos supor que vector<bool> queira guardar 10 bits. Uma implementacao poderia fazer:
Esse vector<bool> poderia ter uma classe representante semelhante a isso:
struct representante
{
representante(char *a, int b): p{a}, posicao{b} {}
char *p;
int posicao;
// ...
representante &operator=(const bool &x);
operator bool();
};
E essa classe representante poderia conter metodos como operator=, etc.
O char *p guardaria qual dos chars de vector contem o bit em questao, e posicao (tb pode ser chamado de offset) guardaria qual posicao dentro daquele char estaria o bit.
Exemplo: se alguem quisesse se referir ao 10o elemento, teria que lidar com o char 1 e com a posicao 6 (contagem da direita para esquerda a partir de 0).
Eu fiz um teste para implementar isso me baseando estruturalmente em um codigo do Bjarne Stroustrup contido no livro PPP2(Programming: Principles and Practice using c++ 2nd edition) capitulo 19:
#include <iostream>
using namespace std;
class Representante
{
char *p;
int offset;
public:
Representante(char *a, int b) : p{a}, offset{b} {}
operator bool() const
{
return (*p & (0x1<<offset))!=0x0;
}
bool operator!=(const Representante &r) const
{
if( (p == r.p) && (offset == r.offset) )
return false;
else
return true;
}
Representante &operator=(const bool x)
{
if(x)
{
*p |= (0x1<<offset);
}
else
{
*p &= ~(0x1<<offset);
}
return *this;
}
Representante &operator++()
{
if(offset)
{
offset--;
}
else
{
p++;
offset = 7;
}
return *this;
}
Representante &operator--()
{
if(offset!=7)
{
offset++;
}
else
{
p--;
offset = 0;
}
return *this;
}
Representante &operator*()
{
return *this;
}
};
class Vectorbool
{
int size; // quantos bits
char *elem; // ponteiro para o vetor de chars que guarda os bits
int space; // quantos bits alocados ao todo (size diz quantos estao sendo usados de fato)
public:
Vectorbool(): size{0}, elem{nullptr}, space{0}
{
// construtor padrao
}
Vectorbool(int n): size{n}, elem{new char[( (size+7)-(size+7)%8 )/8]}, space{( (size+7)-(size+7)%8 )}
{
for(int i=0;i<size;i++)
operator[](i) = false; // operator[] retorna um representante
// o qual tem um operator= para atribuir um bool
}
~Vectorbool()
{
delete[] elem;
}
void reserve(int newalloc) // newalloc deve ser multiplo de 8
{ // se nao for, o reserve forcara que ele seja
// arredondando pra cima de qualquer forma
// newalloc = quantos bits eu quero alocar?
newalloc = (newalloc+7)-(newalloc+7)%8; // forca que seja multiplo de 8
// arredondando pra cima
if(newalloc <= space) // se ja tem espaco entao nao faz nada
return;
char *p1 = new char[newalloc/8]; // aloca a quantidade desejada
for(int i=0;i<size;++i) // copia os elementos antigos
p1[i] = elem[i];
delete[] elem; // desaloca os elementos antigos
elem = p1; // atribui o novo espaco alocado
space = newalloc; // atualiza o valor de space
}
void resize(int newsize)
{
reserve(newsize);
for(int i=size;i<newsize;i++)
operator[](i) = false;
size = newsize;
}
void push_back(bool d)
{
if(space==0)
reserve(8);
else if(size == space)
reserve(2*space);
operator[](size) = d;
size++;
}
Representante operator[](int i) const
{
return Representante {
elem?elem+((i)-(i)%8)/8:nullptr,
elem?(-8-i)%8+7:0
};
}
Representante begin() const
{
return Representante{elem, 7};
}
Representante end() const
{
Representante u{
elem?elem+((size-1)-(size-1)%8)/8:nullptr,
elem?(-7-size)%8+7:0
};
++u;
return u;
}
void debug()
{
cout << "size: " << size << endl;
cout << "space: " << space << endl;
for(Representante a : *this)
{
cout << a << endl;
}
}
};
int main()
{
Vectorbool x(10);
x[0] = false;
x[1] = true;
x[2] = true;
x[3] = true;
x[4] = true;
x[5] = false;
x[6] = true;
x[7] = false;
x[8] = false;
x[9] = true;
x.debug();
return 0;
}
Caso 4: Codigo do Leandro Mello modificado + auto puro + vector<bool> e a recomendacao do Scott meyers
No EMC++, capitulo 2, item 6, Scott Meyers diz:
"Use the explicitly typed initializer idiom when auto deduces undesired types."
Ou seja, peguemos novamente o exemplo proposto por Leandro Melo modificado:
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<bool> vbool;
vbool.resize(10);
bool toggle = true;
for(auto e: vbool)
{
e = toggle;
toggle = !toggle;
}
for(auto e: vbool)
{
cout << e << endl;
}
return 0;
}
O que esta sendo deduzido de auto?
Um desavisado C++ programador diria que e' bool quando na verdade e' vector<bool>::reference (levando em conta o quarto caso da tabela de colisoes), o qual funciona justamente como um "representante" como descrito acima. Se voce rodar esse programa voce observa isso:
1
0
1
0
1
0
1
0
1
0
Se o tipo deduzido de auto fosse realmente bool, voce nao observaria aquela alternancia. Alterando auto para bool, temos o seguinte resultado:
0
0
0
0
0
0
0
0
0
0
No primeiro caso (com auto), temos auto sendo deduzido para vector<bool>::reference e a modificacao (por meio de um operator=) altera de fato o vector, pois o representante contem enderecos do vector.
Ja no segundo caso (com bool), o vector<bool>::reference esta sendo convertido para bool usando operator bool() e atribuido para um tipo bool, e qualquer modificacao nesse bool nao modifica o vector<bool>.
Scott Meyers aconselha Nao usar auto em situacoes onde ele sera deduzido sera um "representante" pois isso pode ficar oculto para quem le o codigo.
A cada iteracao do loop do for e' atribuido um rvalue, infelizmente eu nao consegui repetir isso no meu codigo.
Caso 5: Codigo do Leandro Mello modificado + auto &
Como eu disse no final do ultimo caso, a cada iteracao do loop do for e' atribuido um rvalue. Inclusive eu tentei, sem sucesso, imitar isso, mas nao consegui (se alguem souber modificar meu programa para fazer isso, seria muito interessante).
Usando o auto &, e sabendo que estamos lidando com um vector<bool>::reference, o qual e' um "representante", e sabendo da quinta regra da tabela:'A' & becomes A&Por ultimo e mais interessante:
Entao teremos
//...
for(vector<bool>::reference &e: vbool)
//...
Porem nao e' possivel receber para um lvalue reference um rvalue (Exceto nos casos de const lvalure reference), entao isso resultara em erro de compilacao.
E se mudarmos para bool &?
// ...
for(bool &e: vbool)
// ...
Entao teremos outro erro, pois o operator bool() de vector<bool>::reference criara' um rvalue bool que nao pode ser referenciado por um lvalue reference bool.
Caso 6: Codigo do Leandro Mello modificado + auto &&
Usando o auto &&, e sabendo que estamos lidando com um vector<bool>::reference, o qual e' um "representante", e sabendo da sexta regra da tabela:'A' && becomes A&&
entao teremos auto&& se tornando:
//..
for(vector<bool>::reference &&e : vbool)
//...
Mas veja, a inicializacao da referencia "e" para um objeto rvalue parece uma coisa insegura, pois sendo o rvalue temporario, entao o "e" estaria vinculado a um objeto que ja nao existiria na linha abaixo onde ocorre uma atribuicao para ele:
//...for(vector<bool>::reference &&e: vbool)
{
e = toggle;
toggle = !toggle;
}
//...
Porem, o ISO/IEC 14882:2011, na secao 12.2 Temporary objects, item 4 diz:
There are two contexts in which temporaries are destroyed at a different point than the end of the full-
expression.
=
Ha' dois contextos no qual os temporarios sao destruidos em um ponto diferente do que no fim de uma expressao completa.
Ele continua dizendo:
The first context is [...] (o primeiro contexto nao tem relacao com o problema sendo discutido, portanto sera pulado)
The second context is when a reference is bound to a temporary. The temporary to which the reference is
bound or the temporary that is the complete object of a subobject to which the reference is bound persists
for the lifetime of the reference except:
=
O segundo contexto e' quando uma referencia esta vinculada para um temporario. O temporario para o qual a referencia esta vinculada ou o temporario que e' o objeto completo de um subobjeto para o qual a referencia esta vinculada PERSISTE PARA O TEMPO DE VIDA DA REFERENCIA exceto: [...] (as excecoes nao englobam o que esta sendo discutido aqui).
Portanto, em://...for(vector<bool>::reference &&e: vbool)
{
e = toggle;
toggle = !toggle;
}
//...
O rvalue para o qual a referencia "e" esta vinculado tem o tempo de vida de sua referencia de forma que e' por isso que o codigo acima funciona e e' por isso que metodo do Уθя¡ςκ (auto &&) funciona para o problema colocado por Leandro Melo.
Rodando o codigo com auto &&:
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<bool> vbool;
vbool.resize(10);
bool toggle = true;
for(auto &&e: vbool)
{
e = toggle;
toggle = !toggle;
}
for(auto &&e: vbool)
{
cout << e << endl;
}
return 0;
}
Resultado:
1
0
1
0
1
0
1
0
1
0
.
E se alterarmos de auto && pra bool &&?
Entao estaremos recebendo um vector<bool>::reference rvalue, sobre o qual havera a aplicacao de operator bool(), e um rvalue bool estara' sendo retornado cujo tempo de vida se igualara a sua referencia "e", como descrito no ISO.
Entao a linha
e = toggle;
nao mudara' o vector<bool> de fato, mas sim o rvalue que teve seu tempo de vida aumentado por causa de sua referencia.
Portanto o resultado sera:
0
0
0
0
0
0
0
0
0
0
Conclusoes
A nova tabela proposta conseguiu antecipar todos os casos e e' simples de decorar.
Foi explicado porque o auto && (inicialmente proposto por Уθя¡ςκ) funciona para o problema do vector<bool> (apresentado por Leandro Melo).
Foi dado um exemplo de implementacao de vector<bool> e proxy (representante).
Alguns problemas
O codigo de implementacao de vectorbool+proxy nao esta retornando rvalue a cada iteracao do loop quando aplicado em um "for range based" (ou seja, o for naquele formato --> for(auto x: y) ). Isso nao e' tanto um problema mas esta diferente do comportamento do vector<bool> padrao.
Corrigindo
Quando eu disse:
Nao existe a possibilidade de haver "move" nesse caso particular, e se houvesse (quando ao inves de usar auto && se usasse o tipo unique_ptr<Shape> &&) seria um erro logico que levaria os dois elementos do vetor a abrigar dois nullptr's(internos aos unique_ptr), tornando qualquer tentativa, posterior ao for, de acesso aos dados-membros (ou funcoes virtuais) de objetos apontados por tais enderecos a um erro de dereferencia de ponteiros nullptr.
Eu estava fazendo uma confusao grave entre referencia rvalue e move semantics, aquele e' um instrumento com o qual se pode realizar este, e nao se assemelham.
--
Antes de enviar um e-mail para o grupo leia:
http://www.ccppbrasil.org/wiki/Lista:AntesdePerguntar
--~--~---------~--~----~------------------------------
[&] C & C++ Brasil - http://www.ccppbrasil.org/
Para sair dessa lista, envie um e-mail para ccppbrasil-unsubscribe@googlegroups.com
Eu sempre defendo C++ e vou continuar defendendo.No entanto, uma das coisas que não nego é que é uma linguagem difícil de aprender e cheia de detalhes.Vou colocar um exemplo aqui que não compila, eu sei o motivo, mas vou colocar apenas como exemplo das dificuldades que um iniciante passa.
#include <memory>
#include <vector>class Shape
{
public:
virtual void draw() = 0;
};class Box : public Shape
{
virtual void draw() {}
};class Circle : public Shape
{
virtual void draw() {}
};
int main()
{std::vector<std::unique_ptr<Shape>> v;
v.push_back(std::make_unique<Box>());
v.push_back(std::make_unique<Circle>());
for (auto i : v) {
i->draw();
}
return 0;
}
Eu como novato em C++ deixo minha opinião sobre o assunto da discussão:Eu acredito que um ponto que impacta muito é motivação/propósito. Sentar a bunda e ler 27 capítulos do livro do Bjarne Stroustrup, fazer todos os exercícios e assimilar o conteúdo, e logo depois ler os livros do Scott Meyers... bem não é simples. Eu moro em Brasília e as vagas aqui de c++, além de escassas, são para desenvolvedores pleno/sênior. É extremamente desanimador as oportunidades de c++ por aqui. Aprendi o básico de Python e depois fui até o capítulo 17 do livro do Bjarne no gás, e o que me motivou a aprender c++ e continuava motivando era o fato de poder contribuir com servidores de X jogo em comunidades online.Porém o tempo vai passando e você tem que pagar contas. O mercado só cospe para estágiarios/jrs vagas de .Net/Java/Js/etc, mas nada de c++. Meu primeiro estágio em uma empresa foi de PHP, e eu levava o livro de c++ na mochila. Nesse ponto é onde eu fiquei e acredito que muitos novatos também ficam..."Continuar estudando c++, será que vou encontrar uma oportunidade para aplicar e aprender?"Então, do meu ponto de vista de um novato, aprender c++ corretamente, requer um propósito congruente com a realidade do estudante. Se o mercado não oferece oportunidades para os novatos, dificilmente ele irá se focar em c++, e o rítmo de leitura dos livros irá cair, aproveitando o tempo de estudos para aprender "o que o mercado quer".
Foi basicamente o que aconteceu comigo, meu ritmo de leitura dos livros de c++ caiu drasticamente, e fui aprender Javascript, Python/Django e até tentei me aventurar com Java, mas não gostei de Java.Hoje em dia, foi me dada uma oportunidade de Jr. para aplicar e aprender meus conhecimentos de C++/C#. O cenário é outro, estou extremamente motivado para terminar os livros de c++ e como sou bastante inclinado para a área de segurança, já estou pretendendo ler um livro de Assembly. É realmente muito gratificante entender como as coisas funcionam por baixo dos panos.Do ponto de vista de um novato, é isso ;)