Problema: Eu tenho um object não copiável com dois construtores. Eu preciso criar um object com um dos construtores e usá-lo dentro de algum código comum:
Com um object copiável, ficaria assim e seria fácil:
Object a; if (condition) a = Object(p1); else a = Object(p2,p3,p4); a.doSomething();
Mas, o object não é copiável, então eu tive que fazer isso:
boost::scoped_ptr a; if (condition) a = new Object(p1); else a = new Object(p2,p3,p4); a->doSomething();
Isso parece muito complexo. Existe uma solução melhor?
Aqui está um hack muito terrível, assumindo que Object
é construtível por padrão:
Object a; a.~Object(); if (condition) { ::new (&a) Object(p1); } else { ::new (&a) Object(p2, p3, p4); }
Não use isso.
Outra opção é usar uma união, mas você precisará invocar o destruidor manualmente nessa configuração também.
Uma solução mais limpa poderia ser alcançada com Boost.Optional (usando fábricas no local ). (Obrigado ao @K-Ballo por desenterrar os detalhes!)
#include #include struct Object { explicit Object(int) {} explicit Object(int, float, std::string) {} Object(Object const &) = delete; Object(Object &&) = delete; Object & operator=(Object const &) = delete; Object & operator=(Object &&) = delete; }; boost::optional
Parece perfeitamente razoável para mim do jeito que é. É claro, simples e relativamente conciso.
auto const doSomethingTo = []( Object&& o ) { o.doSomething(); }; doSomethingTo( condition? Object( p1 ) : Object( p1, p2, p3 ) );
Disclaimer: código não tocado pelo compilador.
EDIT : o código acima, quando o construtor Object( Object&& )
é private
, falha ao compilar com o MSVC 11.0 (sim, mesmo no CTP de novembro do ano passado), mas compila bem com o MinGW g ++ 4.7.1 e com alguma versão do clang .
Parece que deve compilar .
Então, é provavelmente um bug no Visual C ++ – mas infelizmente não encontrei uma solução fácil.
Uma solução difícil para o erro do compilador Visual C ++ presumido:
#include #include using namespace std; class Object { private: Object( Object const& ); Object( Object&& ); public: void doSomething() const {} Object( int ) {} Object( int, int, int ) {} }; int main( int argc, char* argv[] ) { int p1 = 0, p2 = 0, p3 = 0; bool condition = argc == 2; auto const doSomething1 = [=]() { Object o( p1 ); o.doSomething(); }; auto const doSomething2 = [=]() { Object o( p1, p2, p3 ); o.doSomething(); }; if( condition ) { doSomething1(); } else { doSomething2(); } }
Outra resposta afirma que o new
(leia-se: uma alocação dinâmica) é sua única opção.
Isto é errado.
Não há realmente nada de errado com a sua solução, embora, como outros já mencionaram, seria mais legível se você usasse o operador condicional em vez de se fosse. Mas você deve considerar a possibilidade de refatoração. Se você fatorar todo o código que usa o object em uma function separada (tomando o object por referência), então algo como:
if ( condition ) { Object a( p1 ); doWhatever( a ); } else { Object a( p2, p3, p4 ); doWhatever( a ); }
pode ser preferível (ou não – não acho que haja uma resposta “certa” em relação à escolha entre esses dois).
Eu não vejo a complexidade … Se você precisa construir eficientemente com base em uma condição se declarar um ponteiro e usar o novo é sua única opção. O que você não precisa necessariamente fazer é:
EDIT: adicionado “eficientemente” na segunda frase.
Eu acho que seu código está OK.
Você só pode querer considerar o operador condicional ? :
? :
boost::scoped_ptr
Então, aqui está um truque rápido para fazer isso funcionar, sem construir objects manualmente. Em vez disso, criei um modelo Deferred
que representa um object no armazenamento automático cuja construção é adiada (e possivelmente nunca ocorre).
O buff
em Deferred
deve ser substituído por uma union
, porque isso tratará de problemas de alinhamento (supondo que você tenha resources do C ++ 11 para suportar isso). Afirma que a constructed
é verdadeira quando você chama get()
é provavelmente uma boa idéia.
Uma vez que você construiu seu object, você pode implicitamente converter seu Deferred
para um T&
, então usar esse T&
como um alias para o T
construído deferido.
Teoricamente, você poderia acabar com o bool
constructed
se pudesse provar que ele seria sempre construído, mas eu aconselharia contra ele. Fora isso, isso deve ser quase tão eficiente quanto você conseguir. E com o caso da union
C ++ 11, pode até ser compatível com os padrões.
Ah, sim, e isso deve ser aprimorado com um encaminhamento perfeito.
#include // does not handle alignment issues: template struct Deferred { Deferred():constructed(false) {} operator T&() { return get(); } T& get() { return *reinterpret_cast(&buff[0]); } template T& construct( Args... args ) { new(&buff[0]) T(args...); constructed = true; return get(); } ~Deferred() { if (constructed) { get().~T(); } } private: bool constructed; char buff[sizeof(T)]; }; #include struct Object { bool is_int; Object( int x ):is_int(true) {} Object( double d ):is_int(false) {} ~Object() { std::cout << "~Object("< o; if(v==as_int) { o.construct( 7 ); } else if (v==as_double) { o.construct( 7.0 ); } else { } } int main() { test(as_int); test(as_double); test(do_not); }