2010-02-05 11 views
11

Me gustaría definir una función de plantilla simple que toma un valor de tiempo de ejecución y determina si es miembro de algún conjunto de valores posibles.C++: ¿Hay alguna manera de definir una matriz estática en línea?

Uso:

int x; // <- pretend this came from elsewhere... 
if (isoneof(x, {5,3,9,25}) ... 

Algo así como:

template <typename T, size_t size> 
bool isoneof(T value, T (&arr)[size]) 
{ 
    for (size_t i = 0; i < size; ++i) 
     if (value == arr[i]) 
      return true; 
    return false; 
} 

Asumo que esto está condenado al fracaso, ya que no veo cómo se puede crear una matriz en línea estática.

que puede utilizar:

int kPossibilities[] = {5,3,9,25}; 
if (isoneodf(6, kPossibilities)) ... 

Con un pequeño cambio en isoneof:

template <typename T1, typename T2, size_t size> 
bool isoneof(T1 value, const T2 (&arr)[size]) 
{ 
    for (size_t i = 0; i < size; ++i) 
     if (value == arr[i]) 
      return true; 
    return false; 
} 

Lo que también hace que sea un poco más flexible.

¿Alguien tiene una mejora para ofrecer? ¿Una mejor manera de definir un "conjunto de valores estáticos en línea"?

+6

Es cuestión de tiempo para C++ 1x, ¿no es así? Los literales de matriz son perfectamente válidos allí ... – xtofl

+0

@xtofl, ya es hora ... –

+0

¡Estamos deseando que llegue! ;) – Mordachai

Respuesta

12

Si te gustan tales cosas, entonces serás un usuario muy feliz de Boost.Assign.

Boost.Assign realidad demuestra que este tipo de semántica son posible, sin embargo, un vistazo a la fuente de asignar le convencerá de que usted no quiere hacer eso por ti mismo :)

usted será capaz de crear algo como esto, sin embargo:

if (isoneof(x, list_of(2)(3)(5)(7)(11)) { ... 

... siendo que tendría que utilizar boost::array como parámetro en lugar de una matriz integrada (gracias, Manuel) el lado negativo - sin embargo, que es un buen momento para en realidad empiece a usarlos:>

+0

Me temo que estás equivocado. Boost.Assign solo funciona con boost :: array, no con arreglos incorporados. Por lo tanto, el OP tendría que cambiar su función para tomar una orden "const boost :: array & arr" ir para poder usar "list_of" como usted sugiere. – Manuel

+0

@Manuel - No dije que no tendría que cambiar nada: P –

+0

@ Kornel: suficiente. Bajaría el tono de mi comentario pero parece que no puedo editarlo. – Manuel

1

¿Éste?

int ints[] = {2,3,5,7,11}; 
#define ARRAY_SIZE(Array) (sizeof(Array)/sizeof((Array)[0])) 
#define INLIST(x,array) isoneof(x,array,ARRAY_SIZE(array)) 

ADEMÁS:

template <typename T> 
bool isoneof(const T& x, T *array, int n) 
{ 
     for(int i=0; i<n; ++i) 
       if(x==array[i]) 
         return true; 
     return false; 
} 
+3

Notinlist escribiendo una macro INLIST, oh la ironía;) –

+1

Esto es un paso * atrás * de lo que el propio OP sugirió, sin embargo. La versión de OP está garantizada para deducir el tamaño de la matriz de forma adecuada (o no compilar), mientras que esto solo produciría resultados incorrectos si la matriz se hubiera descompuesto en puntero. – UncleBens

+0

change (Array) [0] to 0 [Array] para que detecte los tipos que anulan el operador [] –

5

Es posible en el próximo estándar de C++.

Hasta entonces, puedes evitarlo, por ejemplo. sobrecarga operator, para un objeto estático que inicia una matriz estática.

Nota: esta implementación es O (n^2) y puede optimizarse, es solo para captar la idea.

using namespace std; 

template< typename T, size_t N > 
struct CHead { 
    T values[N]; 
    template< typename T > CHead<T,N+1> operator,(T t) { 
     CHead<T,N+1> newhead; 
     copy(values, values+N, newhead.values); 
     newhead.values[N]=t; 
     return newhead; 
    } 
    bool contains(T t) const { 
     return find(values, values+N, t) != values+N; 
    } 
}; 

struct CHeadProto { 
    template< typename T > 
    CHead<T,1> operator,(T t) { 
    CHead<T,1> h = {t}; 
    return h; 
    } 
} head; 



int main() 
{ 
    assert((head, 1,2,3,4).contains(1)); 
    return 0; 
} 
+2

+1: para el código, pero aunque es bueno, no sobrecargo descuidadamente el operador ',' solo para azúcar sintáctico:> –

+0

¡Interesante enfoque! La copia() me encoge lo suficiente como para pensar que buscaría una solución en otro lado. Gracias :) – Mordachai

+0

@Mordachai: echa un vistazo a Boost.Assign si encuentras este código interesante. Muchas cosas igualmente geniales allí. – Manuel

2

Para completar, publicaré una solución que utiliza Boost.MPL. Lo siguiente funciona, pero creo que la solución de Kornel es la mejor.

#include <iostream> 
#include <boost/mpl/for_each.hpp> 
#include <boost/mpl/vector_c.hpp> 

struct Contains 
{ 
    Contains(int value, bool& result) : value(value), result(result) 
    { 
     result = false; 
    } 

    template< typename T > void operator()(T x) 
    { 
     result = result || (x == value); 
    } 

    int value; 
    bool& result; 
}; 


template <class IntList> 
bool isoneof(int val) 
{ 
    namespace mpl = boost::mpl; 
    bool result; 
    mpl::for_each<IntList>(Contains(val, result)); 
    return result; 
} 


int main() 
{ 
    namespace mpl = boost::mpl; 
    std::cout << isoneof< mpl::vector_c<int, 1,2,3,5,7,11> >(4) << "\n"; 
    std::cout << isoneof< mpl::vector_c<int, 1,2,3,5,7,11> >(5) << "\n"; 
} 

Como se puede ver, la matriz de tiempo de compilación se pasa en línea como un argumento de plantilla para isoneof.

+0

¡Genial! Esto parece ser un enfoque sólido también. Resulta en un algoritmo de tiempo de ejecución recursivo, ¿no? – Mordachai

+0

En cuanto a boost/mpl/for_each.hpp, veo que llama 'for_each_impl :: execute()' recursivamente. Dado que todo está en línea, ¿quizás los optimizadores ya son lo suficientemente inteligentes como para "desenrollar" las llamadas recursivas? Tal vez no. No lo sé. :-) –

+0

También tenga en cuenta que la comparación se realizará para todos los elementos en el mpl :: vector_c. No se "romperá" en el primer partido. –

1

Usando C++ 11, esto se escribiría así:

template <typename T> 
bool isoneof(T value, std::initializer_list<T> arr) 
{ 
    using namespace std; 

    return any_of(begin(arr), end(arr), [&](const T& x) { return x == value; }); 
} 
+0

Gracias. Sí, C++ 11 (y 14) hacen que este tipo de cosas sea un poco más agradable. – Mordachai

0

Para su información - He resuelto mi problema en particular el uso de plantillas vararg y listas de inicializador:

template <typename T, typename U> 
bool isoneof(T v, U v1) { return v == v1; } 

template <typename T, typename U, typename... Args> 
bool isoneof(T v, U v1, Args ... others) { return isoneof(v, v1) || isoneof(v, others...); } 

template <typename T, typename U> 
bool isoneof(T value, std::initializer_list<U> values) 
{ 
    for (const auto & e : values) 
     if (value == e) 
      return true; 
    return false; 
} 
Cuestiones relacionadas