Recentemente, encontrei algo semelhante às seguintes linhas:
#include <string>
// test if the extension is either .bar or .foo
bool test_extension(const std::string& ext) {
return ext == ".bar" || ".foo";
// it obviously should be
// return ext == ".bar" || ext == ".foo";
}
Obviamente, a função não faz o que o comentário sugere. Mas esse não é o ponto aqui. Observe que esta não é uma duplicata de Você pode usar 2 ou mais condições OR em uma instrução if? desde que eu estou ciente de como você escreveria a função corretamente!
Comecei a me perguntar como um compilador poderia tratar esse trecho. Minha primeira intuição seria que isso seria compilado return true;
basicamente. Ao incluir o exemplo no godbolt , mostrou que nem o GCC 9.2 nem o clang 9 fazem essa otimização com otimização -O2
.
No entanto, alterar o código para 1
#include <string>
using namespace std::string_literals;
bool test_extension(const std::string& ext) {
return ext == ".bar"s || ".foo";
}
parece fazer o truque, já que a montagem agora é essencialmente:
mov eax, 1
ret
Então, minha pergunta principal é: existe algo que eu perdi que não permite que um compilador faça a mesma otimização no primeiro trecho?
1 Com ".foo"s
isso nem compilaria, pois o compilador não deseja converter um std::string
para bool
;-)
Editar
O seguinte trecho de código também é otimizado "corretamente" para return true;
:
#include <string>
bool test_extension(const std::string& ext) {
return ".foo" || ext == ".bar";
}
fonte
string::compare(const char*)
tem alguns efeitos colaterais que o compilador não eliminará (queoperator==(string, string)
não possui)? Parece improvável, mas o compilador já determinou que o resultado é sempre verdadeiro (também temmov eax, 1
ret
) mesmo para o primeiro trecho.operator==(string const&, string const&)
sejanoexcept
enquantooperator==(string const&, char const*)
não é? Não tenho tempo para aprofundar isso agora.foo || ext == ".bar"
, a chamada é otimizada (consulte editar). Isso contradiz sua teoria?a || b
significa "avaliar a expressãob
apenas se a expressãoa
forfalse
". É ortogonal ao tempo de execução ou tempo de compilação.true || foo()
pode ser otimizado paratrue
, mesmo quefoo()
tenha efeitos colaterais, porque (não importa se otimizado ou não) o lado direito nunca é avaliado. Masfoo() || true
não pode ser otimizado, atrue
menos que o compilador possa provar que a chamadafoo()
não tem efeitos colaterais observáveis.xor eax,eax
, embora sem essa opção chame a função de comparação de cadeias. Não tenho ideia do que fazer com isso.Respostas:
Isso vai surpreender ainda mais a sua cabeça: o que acontece se criarmos um tipo de caractere personalizado
MyCharT
e o usarmos para criar nosso próprio costumestd::basic_string
?Certamente, um tipo personalizado não pode ser otimizado mais facilmente do que um simples
char
, certo?Aqui está a montagem resultante:
Veja ao vivo!
Então, qual é a diferença entre o meu tipo de string "personalizado" e
std::string
?Otimização de pequenas cadeias
Pelo menos no GCC, a Small String Optimization é realmente compilada no binário do libstdc ++. Isso significa que, durante a compilação de sua função, o compilador não tem acesso a essa implementação, portanto, não pode saber se há efeitos colaterais. Por esse motivo, não é possível otimizar a chamada para
compare(char const*)
ausência. Nossa classe "personalizada" não tem esse problema porque o SSO é implementado apenas para simplesstd::string
.BTW, se você compilar
-std=c++2a
, o compilador o otimiza . Infelizmente, ainda não sou experiente o suficiente em C ++ 20 para saber quais mudanças tornaram isso possível.fonte