AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 78011571
Accepted
Jeroen Boschma
Jeroen Boschma
Asked: 2024-02-17 17:23:42 +0800 CST2024-02-17 17:23:42 +0800 CST 2024-02-17 17:23:42 +0800 CST

O destruidor é eliminado quando o construtor está ausente?

  • 772

O código abaixo reproduz o problema que tenho, MSVC 2022:

#include <iostream>

struct A {
    static void message()
    {
        std::cout << "A::message()\n";
    }

    struct B {
        B() {}
        ~B()
        {
            A::message();
        }
    };

    static inline B b;
};
 
int main()
{
}

Com o código acima vejo, como esperado, a mensagem aparecendo. No entanto:

  1. Se eu remover o construtor B(), o destruidor ~B()parece ter sido eliminado porque a mensagem desaparece.
  2. Se eu adicionar uma propriedade fictícia int dummy;à estrutura B, a mensagem aparecerá novamente.

Perguntas para as quais realmente não consegui encontrar resposta:

  1. Parece-me que sem o construtor B()e sem propriedades B, o compilador simplesmente rotula a classe Bcomo vazia e simplesmente a ignora completamente. Isso está correto?
  2. Está em algum lugar documentado que isso pode ocorrer? Dado o conteúdo do destruidor, acho que isso nunca deveria acontecer.
  3. Como posso garantir que sempre funcionará corretamente, não apenas para o compilador que estou usando no momento. Definir o construtor é B()suficiente ou devo também tornar Bnão vazio com a propriedade fictícia int dummy;?
c++
  • 1 1 respostas
  • 78 Views

1 respostas

  • Voted
  1. Best Answer
    user17732522
    2024-02-17T17:50:22+08:002024-02-17T17:50:22+08:00

    A diferença está em ter ou não binicialização estática .

    Se uma variável de duração de armazenamento estático tiver inicialização estática, ela será inicializada antes de qualquer outra variável de duração de armazenamento estático sem inicialização estática (ou seja, inicialização dinâmica ).

    Em particular, há outro objeto importante de duração de armazenamento estático definido em <iostream>, ou seja, uma instância de std::ios_base::Init. A inicialização de um objeto desse tipo causa a inicialização dos fluxos IO padrão (por exemplo, std::cout). Ele também é responsável por liberar todos esses fluxos quando a última instância deles for destruída.

    Portanto, para ver sua saída, você deve ter certeza de que o std::ios_base::Initobjeto foi destruído após seu arquivo b. No entanto, a ordem de destruição é inversa à ordem de inicialização. Portanto, se btiver inicialização estática, mas o std::ios_base::Initobjeto tiver inicialização dinâmica, então é o contrário. Se bnão tiver inicialização estática, então será ordenado corretamente, SOMENTE se ba definição de existir em cada unidade de tradução que inclui (direta ou indiretamente) <iostream>, após a primeira inclusão. Isso ocorre porque você usou inline, o que faz com que a inicialização dinâmica seja bapenas parcialmente ordenada .

    Geralmente, se a inicialização de uma variável estática de duração de armazenamento for uma expressão constante, então a variável terá inicialização estática. Esse é o caso aqui se você não declarar nenhum construtor.

    Se a inicialização não for uma expressão constante, então ela geralmente tem inicialização dinâmica, exceto que, desde que não altere os valores resultantes das variáveis ​​estáticas de duração de armazenamento após sua inicialização, a implementação é livre para escolher a inicialização estática para qualquer tal variável. Se você usar um não constexprconstrutor ou deixar um membro não inicializado, você terá esta situação.

    Portanto, para obter o pedido correto com segurança, você precisa garantir que bnão haja inicialização estática. Devido à margem de manobra dada à implementação, isso é um pouco complicado. Fazer algo no Bconstrutor do que obviamente só pode acontecer em tempo de execução deve ser suficiente (por exemplo, chamar algum IO), mas da forma como o padrão especifica isso atualmente, é difícil ter certeza absoluta porque as diferenças nos efeitos colaterais não são consideradas. Consulte a edição aberta 1294 do CWG .

    Uma alternativa melhor pode ser adicionar um std::ios_base::Initobjeto como membro a B, para que um desses objetos seja certamente mantido ativo enquanto bo destruidor de está em execução.

    Além disso, como você pode ver acima, a construção e destruição de variáveis ​​​​de duração de armazenamento estático em C++ é bastante complicada. Se você puder, seria muito mais simples simplesmente declarar bcomo uma staticvariável não local em main. Geralmente não há nenhuma garantia de que a static inlinevariável seria construída antes da primeira não inicialização ou uso de mainqualquer maneira (isso é definido pela implementação).


    Observe que não verifiquei se essa é realmente a causa no caso do MSVC. A resposta é baseada em considerações teóricas do padrão e no que uma implementação poderia fazer.

    Além disso, estou usando minha interpretação de [basic.start.term]/3 , que li como significando que a ordem de destruição das variáveis ​​inicializadas estaticamente é ordenada com as variáveis ​​inicializadas dinâmicas de acordo com sua ordem real de inicialização. Outra interpretação que pude ver no texto é que a ordem de destruição é como se todas as variáveis ​​tivessem sido inicializadas dinamicamente de acordo com as regras de ordem de inicialização reversa para inicialização dinâmica. Infelizmente, o texto não me parece claro e pelo menos o GCC e o Clang parecem seguir a última interpretação. Nesse caso você não veria o seu problema.

    • 5

relate perguntas

  • Por que os compiladores perdem a vetorização aqui?

  • Erro de compilação usando CMake com biblioteca [fechada]

  • Erro lançado toda vez que tento executar o premake

  • Como criar um tipo de octeto semelhante a std::byte em C++?

  • Somente operações bit a bit para std::byte em C++ 17?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve