resueltos - reglas de c++
¿Cuáles son la sintaxis de expresión sobre los tipos de soporte de C++? (2)
Los tipos de compuestos se pueden construir utilizando la sintaxis del declarador, que se encuentra en [dcl.decl]
.
Detrás de esta sintaxis hay seis construcciones fundamentales, dentro de las cuales cualquier T
puede ser sustituida por cualquiera de las otras construcciones en la lista. [En lo siguiente, (T)
representa una lista de cero o más tipos (que pueden terminar en ''...''), y <T>
representa una lista de uno o más tipos.]
T // T
T* // pointer to T
T& // reference to T
T[n] // array of size ''n'' of T
T C::* // pointer to C::member of type T
T (T) // function taking ''(T)'' and returning T
EDITAR : El tipo de una especialización de plantilla de clase puede ser subsituido para cualquier T
:
C<T> // specialization of class template C with arguments ''<T>''
Hay combinaciones de lo anterior que producen construcciones que tienen un significado especial:
T (*)(T) // pointer to function taking ''(T)'' and returning T
T (C::*)(T) // pointer to C::member-function taking ''(T)'' and returning T
Además, algunas de las construcciones pueden ser calificadas como CV:
const T // const T
T* const // const pointer to T
T C::* const // const pointer to C::member of type T
No todas las combinaciones dan como resultado tipos válidos. Según [basic.compound]
, solo se pueden usar las siguientes combinaciones:
Los tipos compuestos se pueden construir de las siguientes maneras:
- Arreglos de objetos de un tipo dado
- Funciones, que tienen parámetros de tipos dados y devuelven vacíos o referencias u objetos de un tipo dado
- punteros para anular u objetos o funciones (incluidos miembros estáticos de clases) de un tipo determinado
- Referencias a objetos o funciones de un tipo dado.
- punteros a miembros de clase no estáticos, que identifican miembros de un tipo dado dentro de objetos de una clase dada
Se mencionan restricciones adicionales:
[dcl.ptr]
no hay punteros a referencias
[dcl.ref]
No habrá referencias a referencias, ni matrices de referencias, ni punteros a referencias
[dcl.mptr]
Un puntero a miembro no[dcl.mptr]
a ... un miembro con tipo de referencia, o "cv void".
[dcl.fct]
La lista de parámetros (nula) es equivalente a la lista de parámetros vacía. Excepto en este caso especial, void no será un tipo de parámetro. ... Si el tipo de un parámetro incluye un tipo de la forma "puntero a matriz de límite desconocido de T" o "referencia a matriz de límite desconocido de T", el programa está mal formado. Las funciones no tendrán un tipo de tipo de matriz o función de retorno.
Algunas de las posibles construcciones no se pueden usar como argumentos de plantilla. Cuando especifica explícitamente un conjunto de argumentos de plantilla, el compilador debe verificar que los argumentos de plantilla puedan sustituirse por los parámetros de plantilla sin que se obtenga un "tipo no válido". De acuerdo con [temp.deduct]/2
, las siguientes construcciones constituyen tipos no válidos:
La deducción de tipos puede fallar por las siguientes razones:
Intentar crear una matriz con un tipo de elemento que es nulo, un tipo de función o un tipo de referencia, o intentar crear una matriz con un tamaño que sea cero o negativo.
template <class T> int f(T[5]); int I = f<int>(0); int j = f<void>(0); // invalid array
Intentar usar un tipo que no sea un tipo de clase en un nombre calificado.
template <class T> int f(typename T::B*); int i = f<int>(0);
Intentar usar un tipo en la parte de calificador de un nombre calificado que nombre un tipo cuando ese tipo no contiene el miembro especificado, o si el miembro especificado no es un tipo donde se requiere un tipo.
template <class T> int f(typename T::B*); struct A {}; struct C { int B; }; int i = f<A>(0); int j = f<C>(0);
Intentando crear un puntero al tipo de referencia.
Intentar crear una referencia a un tipo de referencia o una referencia a nula.
Intentar crear "puntero a miembro de T" cuando T no es un tipo de clase.
template <class T> int f(int T::*); int i = f<int>(0);
Intentar realizar una conversión no válida en una expresión de argumento de plantilla o una expresión utilizada en la declaración de función.
template <class T, T*> int f(int); int i2 = f<int,1>(0); // can’t conv 1 to int*
Intentar crear un tipo de función en el que un parámetro tiene un tipo de vacío.
- Intentando crear un tipo de función calificado por el CV.
EDITAR: Esto se basa en C ++ 03, pero también se aplica a C ++ 11 (que agrega tipos de referencia de rvalue)
Estaba trabajando con una clase de plantilla que toma un conjunto de enteros. El código era como,
template<unsigned... Idx>
struct work{ ... };
Luego me di cuenta de que el usuario puede necesitar proporcionar un conjunto de enteros o un rango de enteros. Entonces, cambié poco la sintaxis para soportar la creación de instancias como,
work<indexes<1,3,2,4> > //instead of work<1,3,2,4>
work<span<1,4> > //same as work<1,2,3,4>
Mientras que en C ++ tenemos un gran número de operadores, y se pueden utilizar para formular plantillas de expresiones exóticas (por ejemplo, boost::xpressive
, boost::lambda
, boost::spirit
etc.), las posibilidades para la manipulación de tipos son mucho menores.
En una nota clave de boostcon por Sean Parent, observó que aún no se puede escribir pair<int>
para denotar a pair of integers
. En mi biblioteca personal, hice una sintaxis como tuple<int[3]>
para indicar una tupla de 3 enteros, en lugar de escribir una tupla con 3 int en los argumentos de tipo, observando que no escribo una matriz en bruto como argumento de tupla ¡en cualquier sitio! (nota: std::array<int,3>
no es lo mismo que anteriormente, ya que std :: array no puede almacenar referencias mientras que tuple
puede, digamos std::tuple<int&,int&,int&>
es posible)
Entonces, quiero saber cuáles son los diferentes tipos de "expresiones de tipo" que puedo escribir.
Hasta ahora puedo pensar en el tipo de objeto, tipo de función, tipo de referencia, con / sin modificadores cv, punteros, etc.
template<class T>
struct tpl;
using t1 = tpl<int>;//simple type
//function (can have function pointer/reference also)
// e.g. void(*)(int,float) or void(&)(int,float)
using t2 = tpl<void(int,float)>;
//array can have pointer / reference also
//e.g. int(&)[4] or int (*)[4]
using t3 = tpl<int[4]>;
using t4 = tpl<int[]>;
using t5 = tpl<int const>;//with cv modifiers
using t6 = tpl<int*>;//with pointer
using t7 = tpl<int&>;//with reference (& or &&)
using t8 = tpl<tpl<int> >; //template itself
using t9 = tpl<void(...)>; //variadic functions
using t10 = tpl<R C::*>; //pointer to member
Pero creo que, muchos más son posibles.
NOTA: esta pregunta es puramente teórica, solo quiero saber todo tipo de sintaxis que puedo escribir dentro de <> como tipo de argumento , y no sobre el aspecto de legibilidad / moralidad de la misma, o incluso cómo puedo implementar algunos de los ejemplos que tenía Dado, como la clase obrera.
No estoy completamente seguro de lo que estás preguntando. Pensé que la muestra que diste era interesante y jugué un poco con ella.
Se me ocurrió una implementación que hace que span<a,b>
sea un alias de plantilla para los indexes<a, ..., b>
, utilizando el truco de la deducción de tipos en constexpr
funciones constexpr
:
template <int a, int b> using span = decltype(expand_span<a,b>());
Ahora puedes tener tu pastel y comerlo:
////////////////////////////////////////////////////////////////
// using indirect template arguments
template<typename> struct indirect_work { };
void test_indirect()
{
indirect_work<indexes<1,2,3,4>> x;
indirect_work<span<1,4>> y;
x = y; // x and y are of identical types
static_assert(std::is_same<indexes<1,2,3,4>, span<1,4>>::value, "fact check");
}
Pero, quizás más interesante, aún puede hacer que su plantilla de work
principal tome una lista de argumentos de plantilla <int...>
procesar :
////////////////////////////////////////////////////////////////
// using direct template arguments
template<int...> struct direct_work { };
// deduction alias:
template<int... direct> constexpr direct_work<direct...> deduction_helper(indexes<direct...>);
template <typename Idx> using deduce = decltype(deduction_helper(Idx{}));
void test_direct()
{
direct_work<1,2,3,4> x;
deduce<indexes<1,2,3,4>> y;
deduce<span<1,4>> z;
static_assert(std::is_same<decltype(x), decltype(y)>::value, "fact check");
static_assert(std::is_same<decltype(x), decltype(z)>::value, "fact check");
}
Vea una demostración de trabajo completa aquí: gcc en ideone . Lo compilé con clang localmente.
Codigo completo
Código para expand_span
duplicado aquí en caso de que el enlace se quede muerto:
#include <type_traits>
template <int...> struct indexes {};
namespace {
template<int a, int... other>
constexpr indexes<a, other...> combine(indexes<other...> deduce);
template<int a, int b, typename Enable = void> struct expand_span_; // primary
template<int a, int b>
struct expand_span_<a, b, typename std::enable_if< (a==b), void >::type> {
static constexpr indexes<a> dispatch();
};
template<int a, int b>
struct expand_span_<a, b, typename std::enable_if< (a<b), void >::type> {
static constexpr decltype(combine<a>(expand_span_<a+1, b>::dispatch()))
dispatch();
};
template<int a, int b>
constexpr auto expand_span() -> decltype(expand_span_<a,b>::dispatch());
}
template <int a, int b> using span = decltype(expand_span<a,b>());
////////////////////////////////////////////////////////////////
// using indirect template arguments
template<typename> struct indirect_work { };
void test_indirect()
{
indirect_work<indexes<1,2,3,4>> x;
indirect_work<span<1,4>> y;
x = y; // x and y are of identical types
static_assert(std::is_same<indexes<1,2,3,4>, span<1,4>>::value, "fact check");
}
////////////////////////////////////////////////////////////////
// using direct template arguments
template<int...> struct direct_work { };
// deduction alias:
template<int... direct> constexpr direct_work<direct...> deduction_helper(indexes<direct...>);
template <typename Idx> using deduce = decltype(deduction_helper(Idx{}));
void test_direct()
{
direct_work<1,2,3,4> x;
deduce<indexes<1,2,3,4>> y;
deduce<span<1,4>> z;
static_assert(std::is_same<decltype(x), decltype(y)>::value, "fact check");
static_assert(std::is_same<decltype(x), decltype(z)>::value, "fact check");
}
int main()
{
test_indirect();
test_direct();
}