Isso é válido em C ++?
int main() {
constexpr auto sz = __func__ - __func__;
return sz;
}
O GCC e o MSVC acham que está tudo bem, Clang acha que não: o Compiler Explorer .
Todos os compiladores concordam que este está correto: Explorer do compilador .
int main() {
constexpr auto p = __func__;
constexpr auto p2 = p;
constexpr auto sz = p2 - p;
return sz;
}
Clang novamente não gosta deste, mas os outros estão bem com ele: Explorador de Compiladores
int main() {
constexpr auto p = __func__;
constexpr auto p2 = __func__;
constexpr auto sz = p2 - p;
return sz;
}
O que há aqui em cima? Eu acho que a aritmética em ponteiros não relacionados é um comportamento indefinido, mas __func__
retorna o mesmo ponteiro, não? Não tenho certeza, então pensei em testá-lo. Se bem me lembro, std::equal_to
pode comparar ponteiros não relacionados sem comportamento indefinido:
#include <functional>
int main() {
constexpr std::equal_to<const char*> eq{};
static_assert(eq(__func__, __func__));
}
Clang acha que eq(__func__, __func__)
não é uma expressão constante, mesmo que std::equal_to::operator()
seja consexpr . Outros compiladores não reclamam: Compiler Explorer
Clang também não compilará este. Reclama que __func__ == __func__
não é uma expressão constante: Compiler Explorer
int main() {
static_assert(__func__ == __func__);
}
__func__
é tão-sestatic const char __func__[] = "function-name";
e que equivale é aceito demo ...__func__
e uso que no static_assert ...__func__
inteiramente da avaliação constexpr.Respostas:
__func__
em C ++ é um identificador. Em particular, ele faz referência a um objeto específico. De [dcl.fct.def.general] / 8 :Como uma variável predefinida local da função , essa definição (como se) aparece no início do bloco da função. Como tal, qualquer uso
__func__
dentro desse bloco se referirá a essa variável.Quanto à parte "qualquer outro objeto", uma variável define um objeto.
__func__
nomeia o objeto definido por essa variável. Portanto, dentro de uma função, todos os usos do__func__
nome da mesma variável. O que é indefinido é se essa variável é um objeto distinto de outros objetos.Ou seja, se você estiver em uma função nomeada
foo
e tiver usado o literal em"foo"
algum outro lugar do problema, não é proibido que uma implementação faça com que a variável__func__
também seja o mesmo objeto que o literal"foo"
retorna. Ou seja, o padrão não exige que todas as funções em que__func__
aparecem devem armazenar dados separados da própria cadeia de caracteres literal.Agora, a regra "como se" do C ++ permite que as implementações se desviem disso, mas elas não podem fazer isso de uma maneira que seria detectável. Portanto, enquanto a variável em si pode ou não ter um endereço distinto de outros objetos, os usos de
__func__
na mesma função devem se comportar como se estivessem se referindo ao mesmo objeto.Clang não parece implementar
__func__
dessa maneira. Parece implementá-lo como se retornasse uma string de valor prvalor literal do nome da função. Dois literais de cadeia de caracteres distintos não precisam se referir ao mesmo objeto; portanto, subtrair ponteiros para eles é UB. E o comportamento indefinido em um contexto de expressão constante é mal formado.A única coisa que me deixa hesitante em dizer que Clang está 100% errado aqui é [temp.arg.nontype] / 2 :
Veja, isso parece permitir alguns enganos pela implementação. Ou seja, embora
__func__
tecnicamente possa ser uma expressão constante, você não pode usá-la em um parâmetro de modelo. É tratado como uma string literal, mesmo que seja tecnicamente uma variável.Então, em algum nível, eu diria que o padrão está falando dos dois lados da boca.
fonte
__func__
pode ser uma expressão constante em todos os casos da minha pergunta, certo? Portanto, o código deveria ter sido compilado.__func__
endereço for igual à de outro objeto, e na segunda ocorrência__func__
não for? É verdade que isso não significa que o endereço seja diferente entre as duas instâncias, mas ainda estou confuso!__func__
não é uma macro; é um identificador que nomeia uma variável específica e, portanto, um objeto específico. Portanto, qualquer uso__func__
na mesma função deve resultar em um valor de gl que se refere ao mesmo objeto. Ou, mais precisamente, não pode ser implementado de tal maneira que não seja esse o caso.new
, ele não será levado a lugar nenhum até o término do programa.