¿Es esto válido C ++?
int main() {
constexpr auto sz = __func__ - __func__;
return sz;
}
GCC y MSVC piensan que está bien, Clang cree que no: Compiler Explorer .
Todos los compiladores están de acuerdo en que este está bien: Compiler Explorer .
int main() {
constexpr auto p = __func__;
constexpr auto p2 = p;
constexpr auto sz = p2 - p;
return sz;
}
A Clang nuevamente no le gusta este, pero los otros están de acuerdo: Compiler Explorer
int main() {
constexpr auto p = __func__;
constexpr auto p2 = __func__;
constexpr auto sz = p2 - p;
return sz;
}
¿Qué hay aquí arriba? Creo que la aritmética en punteros no relacionados es un comportamiento indefinido pero __func__
devuelve el mismo puntero, ¿no? No estoy seguro, así que pensé que podría probarlo. Si recuerdo correctamente, std::equal_to
puedo comparar punteros no relacionados sin un comportamiento indefinido:
#include <functional>
int main() {
constexpr std::equal_to<const char*> eq{};
static_assert(eq(__func__, __func__));
}
Clang piensa eq(__func__, __func__)
que no es una expresión constante, aunque std::equal_to::operator()
es constexpr . Otros compiladores no se quejan: Compiler Explorer
Clang tampoco compilará este. Se queja de que __func__ == __func__
no es una expresión constante: Compiler Explorer
int main() {
static_assert(__func__ == __func__);
}
__func__
es como sistatic const char __func__[] = "function-name";
y ese equivalente es aceptado Demo ...__func__
y la usa en static_assert ...__func__
completo de la evaluación constexpr.Respuestas:
__func__
en C ++ es un identificador. En particular, hace referencia a un objeto específico. De [dcl.fct.def.general] / 8 :Como una variable predefinida de función local , esta definición (como si) apareciera al comienzo del bloque de funciones. Como tal, cualquier uso
__func__
dentro de ese bloque se referirá a esa variable.En cuanto a la parte "cualquier otro objeto", una variable define un objeto.
__func__
nombra el objeto definido por esa variable. Por lo tanto, dentro de una función, todos los usos del__func__
nombre de la misma variable. Lo que no está definido es si esa variable es un objeto distinto de otros objetos.Es decir, si está en una función llamada
foo
y usó el literal"foo"
en otro lugar del problema, no está prohibido que una implementación tenga la variable__func__
también el mismo objeto que el literal"foo"
devuelve. Es decir, el estándar no requiere que cada función en la que__func__
aparece debe almacenar datos separados del literal de cadena en sí.Ahora, la regla "como si" de C ++ permite que las implementaciones se desvíen de esto, pero no pueden hacerlo de una manera que sea detectable. Entonces, mientras que la variable en sí misma puede o no tener una dirección distinta de otros objetos, los usos de
__func__
en la misma función deben comportarse como si se estuvieran refiriendo al mismo objeto.Clang no parece implementarse de
__func__
esta manera. Parece implementarlo como si devolviera un literal de cadena prvalue del nombre de la función. Dos literales de cadena distintos no tienen que referirse al mismo objeto, por lo que restarles punteros es UB. Y el comportamiento indefinido en un contexto de expresión constante está mal formado.Lo único que me hace dudar al decir que Clang está 100% equivocado aquí es [temp.arg.nontype] / 2 :
Mira, esto parece permitir algo de fraude por la implementación. Es decir, aunque
__func__
técnicamente puede ser una expresión constante, no puede usarse en un parámetro de plantilla. Se trata como un literal de cadena, aunque técnicamente es una variable.Entonces, en cierto nivel, diría que el estándar está hablando desde ambos lados de su boca.
fuente
__func__
puede ser una expresión constante en todos los casos en mi pregunta, ¿verdad? Entonces el código debería haberse compilado.__func__
la dirección es la misma que la de otro objeto, y en la segunda__func__
no es así? De acuerdo, eso no significa que la dirección difiera entre las dos instancias, ¡pero todavía estoy confundido!__func__
no es una macro; Es un identificador que nombra una variable específica y, por lo tanto, un objeto específico. Por lo tanto, cualquier uso de__func__
en la misma función debería dar como resultado un valor gl que se refiera al mismo objeto. O más al punto, no se puede implementar de tal manera que este no sea el caso.new
, no irá a ningún lado hasta que finalice el programa.