c++ - Creación de instancias de plantilla con fallo de la función constexpr
templates c++11 (2)
Tengo una plantilla de clase C
que tiene un parámetro de plantilla no de tipo pero de referencia para un tipo P
:
class P {
public:
int x;
int y;
};
template <const P &x>
class C {
public:
const int &f() { return x.x; }
};
Declaré una variable global de tipo P
:
P p = {33,44};
También declaré una función que devuelve una referencia a p
:
constexpr const P &h() { return p; }
Y luego trató de usar estos en los siguientes:
C<p> o; // line 1
C<h()> oo; // line 2
Por supuesto no tengo ningún problema con la primera instanciación, pero la segunda. Mi compilador se queja:
error: non-type template argument does not refer to any declaration
¿Por qué es así? No pude encontrar un argumento en contra en la norma. No estoy seguro de que sea exactamente el mismo problema que en Calling constexpr en el argumento de la plantilla predeterminada , donde la discusión fue sobre el punto de instanciación de la instanciación anidada . Aquí es más un problema de tipo, pero ¿cuál? Mi función h()
devuelve una referencia a una variable bien definida del tipo bien definido ( const P &
). Esperaba que algún alistamiento tuviera lugar para dar el resultado correcto, pero no es el caso. ¿Podría decirme por qué?
Declarar la función como en línea no cambia nada al problema.
Los experimentos se realizaron con Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn
. También probé con g++-mp-4.8 (MacPorts gcc48 4.8.3_2) 4.8.3
y el error se notificó como:
''h()'' is not a valid template argument for type ''const P&'' because it is not an object with external linkage
Parece que mi llamada a h()
(que es un constexpr
modo constexpr
computable) no se ve como tal ...
Olvidé decir que el problema es el mismo si intentamos con otra referencia como esta:
const P &pp = p;
y entonces
C<pp> oo;
esta vez el primer compilador dice:
non-type template argument of reference type ''const P &'' is not an object
y el segundo:
error: could not convert template argument ''pp'' to ''const P &''
pp
no es un objeto? pp
no es de tipo const P&
? Bueno, puedo usarlo como es uno ... Sé que es una referencia pero no se puede distinguir de una referencia nativa, o?
En el caso de
C<h()> oo;
§14.3.2 / 4 patadas en:
[Nota: los temporales, los valores sin nombre y los valores con nombre sin vinculación no son argumentos de plantilla aceptables cuando el parámetro de plantilla correspondiente tiene un tipo de referencia.
(énfasis mío)
Parece que esta restricción estaba sujeta a la siguiente propuesta Permitir evaluación constante para todos los argumentos de plantilla que no son de tipo , aún intentando determinar el estado de esta propuesta. Dice:
Las restricciones sintácticas para los punteros, las referencias y los punteros a los miembros son torpes y evitan refactorizaciones razonables. Por ejemplo:
template<int *p> struct A {}; int n; A<&n> a; // ok constexpr int *p() { return &n; } A<p()> b; // error
y además dice:
La razón histórica de la restricción era más probable que C ++ previamente no tenía una especificación suficientemente sólida para expresiones constantes de tipo puntero, referencia o puntero a miembro. Sin embargo, ese ya no es el caso. El status quo es que se requiere una implementación para evaluar dicho argumento de plantilla, pero luego debe descartar el resultado si resulta que no es nulo.
Además de lo anterior, la restricción a las entidades con vinculación es un artefacto de plantillas exportadas, y podría haberse eliminado cuando se eliminaron las restricciones de vinculación en los parámetros de tipo de plantilla.
y eliminaría esta sección de la nota con esta restricción:
lvalues sin nombre, y lvalues con nombre sin vinculación
toda la nota dice:
Los valores temporales, los valores sin nombre y los valores con nombre sin vinculación no son argumentos de plantilla aceptables cuando el parámetro de plantilla correspondiente tiene un tipo de referencia.
Actualizar
La versión revisada de esta propuesta N4268 fue adoptada en el borrador de trabajo en Urbana y podemos ver los cambios en el último borrador de trabajo N4296 . La nueva nota dice:
Un objeto temporal no es un argumento de plantilla aceptable cuando el parámetro de plantilla correspondiente tiene un tipo de referencia
La sección normativa es 14.3.2
(temp.arg.nontype) párrafo 1
que con esta propuesta diría:
Para un parámetro de tipo de referencia o tipo de puntero que no sea de tipo, el valor de la expresión constante no se referirá a (o para un tipo de puntero, no será la dirección de):
- un subobjeto (1.8),
- un objeto temporal (12.2),
- una cadena literal (2.14.5),
- el resultado de una expresión tipográfica (5.2.8), o
- una variable de función predefinida (8.4.1).
y podemos encontrar esta nueva redacción en el último borrador del estándar N4296 .
Parece que este cambio realmente se ha implementado en clang HEAD
ver su código trabajando en live , usando la -std=c++1z
. Esto implica que el cambio debe ser parte de C ++ 17, suponiendo que ningún cambio posterior lo revierta o lo altera.