Tão confuso com a inicialização da lista cpp e a divergência do compilador.
Isso aconteceu enquanto eu estava revisando alguns recursos do cpp.
U ({ arg1, arg2, ... }) (11)
cppreference #copy-list-initialization case 11
diz
expressão de conversão funcional ou outras invocações de construtor, onde uma lista de inicializadores entre chaves é usada no lugar de um argumento do construtor. A inicialização da lista de cópias inicializa o parâmetro do construtor (observe que o tipo U neste exemplo não é o tipo que está sendo inicializado por lista;
U's constructor's parameter
é)
Então tentei o seguinte código para ver qual construtor o compilador Cpp escolherá:
#include <string>
#include <vector>
#include <map>
#include <tuple>
#include <iostream>
using namespace std;
struct A {
static int id;
A(int integer, float sci, string str, vector<int> vnum, pair<int, int> intP, tuple<int, string, float> isfTuple) {
}
A(pair<int, float> ifP) { // this also allows {} for default construction
}
A(const A &) = delete;
A(A &&) = delete;
~A() {
cout << "Destructed: " << ++id << endl;
}
};
int main(){
A b({1, 1, "str", {1, 2}, {1, 1}, {1, "str", 1}}); // works on MinGW
// MinGW version: x86_64-14.2.0-release-win32-seh-ucrt-rt_v12-rev2
// MSVC error: 'A::A(A &&)': attempting to reference a deleted function
A partial({1, 1.0}); // works both on MinGW and MSVC with ISO C++20 Standard (/std:c++20)
}
No caso em questão A b(...)
, fiquei confuso sobre o motivo pelo qual o compilador funciona mesmo com o construtor de cópia A(const A &) = delete;
e o construtor de movimento A(A&&)
excluídos. Então, pesquisei sobre a conversão de construtores , a elisão de cópia e o rascunho de CPP em inicializadores, mas fiquei ainda mais confuso. Tentei compilar com MSVC e obtive o erro:
'A::A(A &&)': tentando referenciar uma função excluída
Então o Compiler Explorer obteve sucesso.
Então, o que está acontecendo? Bug no compilador MinGW ou MSVC? Ou este é um comportamento indefinido (mas o caso 11 parece estar bem definido)? Por que o MinGW e o Compiler Explorer tiveram sucesso?
Meus scripts de construção do GCC (usando vscode tasks.json):
g++ -Wall --std=gnu++20 -g ${file} -o ${fileDirname}\\${fileBasenameNoExtension}
Plataforma: Windows 11, é claro.
Conjunto de ferramentas do Visual Studio:Visual Studio 2022 (v143)
Padrão de linguagem do Visual Studio:ISO C++20 Standard (/std:c++20)
Resultado do Compiler Explorer
Editar:
Meus testes posteriores mostram que tanto o MSVC quanto o MinGW usam os mesmos construtores para cada caso se nenhum construtor for removido -- Result . O resultado do stdio permanece o mesmo. Há apenas uma divergência no comportamento de verificação do compilador (não sei qual é realmente) e o resultado da compilação é o mesmo, novamente, se nenhum construtor for removido. E sob -std=gnu++11, onde a eliminação de cópia não está disponível até o C++17, tanto o MSVC quanto o MinGW mantêm a mesma saída -- nenhum construtor de cópia/movimentação foi usado.
Então A b...
, na verdade, o caso de uso 1 é uma sintaxe de inicialização direta, em vez do caso 11, o que faz a sintaxe do caso 1 entrar em conflito com o caso de teste, A b...
ou ele está, na verdade, usando outra coisa?
Editar:
Parece que a elisão de cópia sempre acontece desde o C++ 11 durante a inicialização, mas algumas divergências acontecem no processamento de pré-valor na atribuição e na expressão de retorno entre compiladores. Acredito que a melhor prática é evitar aproveitar as vantagens mínimas do efeito colateral dos construtores.
Editar:
Acho que @Jarod42 está certo.