2010-08-09 27 views
7

creo que muchos de ustedes tienen este tipo de código en algún lugar:Usando C++ funciones lambda durante la inicialización variable de

int foo; 
switch (bar) { 
    case SOMETHING: foo = 5; break; 
    case STHNELSE: foo = 10; break; 
    ... 
} 

Pero este código tiene algunos inconvenientes:

  • usted puede olvidarse fácilmente una "ruptura"
  • foo La variable no está const mientras que debería ser
  • es que no es bella

Así que empezaron a preguntarse si había una manera de "mejorar" este tipo de código, y tengo esta pequeña idea:

const int foo = [&]() -> int { 
    switch (bar) { 
    case SOMETHING: return 5; 
    case STHNELSE: return 10; 
    ... 
    } 
}(); 

Nota: el primer par de paréntesis que no es obligatorio, pero MSVC++ no es compatible aún

Puede utilizar el mismo truco con if-else donde el operador ternario sería demasiado complicado, las variables que requieren pasar por punteros para inicializarse (como para funciones de DirectX), etc.

Mi las preguntas son:

  • ¿Hay algún problema con este código que no haya visto?
  • ¿Le parece mejor que la anterior?
  • g ++ parece alinear la función, pero ¿cree que todos los compiladores lo harán?

EDIT: esto es lo que quiero decir con "funciones de DirectX"

_xAudio2 = [&]() -> std::shared_ptr<IXAudio2> { 
    IXAudio2* ptr = nullptr; 
    if (FAILED(XAudio2Create(&ptr, xAudioFlags, XAUDIO2_DEFAULT_PROCESSOR))) 
     throw std::runtime_error("XAudio2Create failed"); 
    return std::shared_ptr<IXAudio2>(ptr, [](IUnknown* ptr) { ptr->Release(); }); 
}(); 
+4

¡Truco limpio! Pero iría un paso más allá y convertiría la lambda en una función llamada "int EnumToFoo (Enum)" de todos modos: menos desorden en la función de llamada y automáticamente documentado con un nombre bien descriptivo;). – Sjoerd

+0

Estoy con sjoerd - Siempre refactorizaría cualquier función de mapeo - cambiaría o de otra manera – StuartLC

+1

En este caso podría valer la pena codificar una función externa, pero no me veo creando una función solo para construir un objeto DirectX por ejemplo – Tomaka17

Respuesta

3

Ésta es una técnica bastante común en otros idiomas. Casi todas las características de alto nivel de Scheme se definen en términos de lambdas que se llaman inmediatamente.

En JavaScript, es la base del "patrón de módulo", p. Ej.

var myModule = (function() { 

    // declare variables and functions (which will be "private") 

    return { 
     // populate this object literal with "public" functions 
    }; 

})(); 

lo tanto una función anónima se declara y llamó inmediatamente, de modo que cualquier detalles internos están ocultos y sólo el valor de retorno está expuesta externamente.

El único inconveniente es que en una lectura casual del código, las instrucciones return parecerán estar regresando de la función externa (hubo una intensa controversia sobre esto durante las guerras Java lambda). Pero esto es algo a lo que debes acostumbrarte una vez que tu lenguaje tenga lambdas.

Hay muchas funciones de lenguaje en un lenguaje imperativo como C++ que se beneficiaría de poder devolver un valor (en lugar de ser como una función void). Por ejemplo, if tiene una alternativa, el operador terciario expr ? a : b.

En Ruby, prácticamente todas las afirmaciones se pueden evaluar, por lo que no hay necesidad de una sintaxis separada donde se pueda proporcionar un valor de retorno.Si C++ funcionara así, esto significaría cosas como:

auto result = try 
{ 
    getIntegerSomehow(); 
} 
catch (const SomeException &) 
{ 
    0; 
} 
+0

Gracias, no estoy tan acostumbrado a usar lenguajes funcionales – Tomaka17

0

que aún no hay razón para usar una caja de conmutación en un caso así. Cualquier compilador decente generará un código tan rápido como if con enunciados como con un caso de switch.

if(bar == SOMETHING) 
    foo = 5; 
else if(bar == STHNELSE) 
    foo = 10; 
Cuestiones relacionadas