Erreur de compilation en C++17 sous Visual Studio Community 2022

a marqué ce sujet comme résolu.

Bonjour, j’essaie de m’entrainer à utiliser les fichiers après avoir lu le chapitre sur les flux du tutoriel disponible sur ce site. J’ai essayé de faire un programme résolvant le problème "3X+1" (on prend un nombre, si celui-ci est pair on le divise par deux, sinon on le multiplie par 3 et on ajoute 1, et ainsi de suite pour finalement arriver à une boucle qui fait 1, 4, 2, 1) avec un système de sauvegarde mais, quand j’essaie de compiler, je reçoit le message d’erreur suivant dans la section output:

Build started… 1>——— Build started: Project: Project1, Configuration: Debug x64 ———

1>Source.cpp

1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\include\xutility(5268,21): error C2446: '==’: no conversion from '_Ty (__cdecl *)' to 'int'

1> with

1> [

1> _Ty=int (int)

1> ]

1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\include\xutility(5268,24): message : There is no context in which this conversion is possible

1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\include\xutility(5277): message : see reference to function template instantiation '_InIt std::_Find_unchecked<char*,_Ty>(_InIt,const _InIt,_Ty (__cdecl &))' being compiled

1> with

1> [

1> _InIt=char *,

1> _Ty=int (int)

1> ]

1>C:\Users*mon nom*\Documents\Programing\Project1\Source.cpp(58): message : see reference to function template instantiation '_InIt std::find<std::_String_iterator<std::_String_val<std::_Simple_types<_Elem»>,int(int)>(_InIt,const _InIt,_Ty (__cdecl &))' being compiled

1> with

1> [

1> _InIt=std::_String_iterator<std::_String_val<std::_Simple_types<char>»,

1> _Elem=char,

1> _Ty=int (int)

1> ]

1>Done building project "Project1.vcxproj" — FAILED.

========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Voici mon code (je sais qu’il y a une boucle infinie, mais ça n’empêche d’habitude pas le code de compiler, et je l’utilise pour répéter l’action à l’infini pour ne pas avoir à relancer le programme à chaque fois):

#include <fstream>
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <string>
#include <cctype>

void calculation(int number, std::string fileName) //makes the calculations
{
   std::ofstream oFile(fileName);

   int generation{ 0 };

   std::cout << number << ", ";

   if (number % 2 == 0)
   {
       number /= 2;
       ++generation;

       if (number == 1)
       {   
           std::cout << number << std::endl << std::endl;

           oFile << generation << std::endl;
       }

       else
       {
           std::cout << ", ";
       }
   }

   else
   {
       number *= 3;
       number++;
       ++generation;

       std::cout << number << ", ";
   }
}

int main()
{
//recuperation of the last processed number
   
   std::ifstream iFile{"save.txt"};
   std::string line;
   std::vector<int> originNbr;
   std::vector<int> generationNbr;

   while (getline(iFile, line))
   {
   //finding the last origin nbr
       std::string strCurrentNbr;
       for (auto i{ std::find(std::begin(line), std::end(line), isdigit) }; isdigit(*i); i++)
       {
           if (isdigit(*i))
           {
               strCurrentNbr.push_back(*i);
           }
       }

       originNbr.push_back(std::stoi(strCurrentNbr));

       for (auto i{ std::find(std::rbegin(line), std::rend(line), isdigit)}; isdigit(*i); i++)
       {
           if (isdigit(*i))
           {
               strCurrentNbr.insert(0, 1, *i);
           }
       }

       generationNbr.push_back(std::stoi(strCurrentNbr));

   }

   std::ofstream oFile{ "save.txt", std::ios::app};
   
   int number;

   if (!(originNbr.empty()))
   {
       number = originNbr.back();
   }

   else
   {
       number = 1;

       oFile << "Origin number: " << number << ", number of generation : " << 1 << std::endl;

       std::cout << 1 << std::endl << std::endl;
   }

//calculation
   while (true)
   {
       if (number != 1)
       {
           calculation(number, "save.txt");
       }

       ++number;
   }

   return 0;
}

Je n’ai pas de message d’erreur dans la section 'erreur’, mais le code ne veut pas compiler, quelqu’un aurait-il une solution?

Merci

+0 -0

Salut,

std::find prend une valeur, pas un prédicat. Les lignes

for (auto i{ std::find(std::begin(line), std::end(line), isdigit) }; isdigit(*i); i++)
// ...
for (auto i{ std::find(std::rbegin(line), std::rend(line), isdigit)}; isdigit(*i); i++)

sont donc invalides. Il faut utiliser find_if à la place. Les templates C++ rendent l’erreur assez imbitable pour être honnête…

EDIT: mal lu la doc…

+0 -0

La joie des erreurs de C++ :lol: Ligne 58, si tu veux chercher dans une collection sur la base du prédicat isdigit, c’est std::find_if qu’il faut utiliser, pas std::find qui prend une valeur.

Et je suis d’accord avec adri1, la boucle a l’air un peu bizarre.

Sinon je pense que tu devrais mettre ta sortie d’erreur dans un bloc de code aussi, ce serait plus facile à lire :)

J’avais mal lu la doc en fait, les templates sont tellement illisibles que j’avais pas vu qu’il y avait find pour les valeurs et find_if pour les prédicats… :-° C’est pas une super idée pour la doc de mettre plusieurs interfaces collées les unes aux autres

EDIT : par ailleurs, le mécanisme de sauvegarde et d’affichage est un peu étrange. Déjà, c’est pas super d’afficher une énorme ligne avec pleins de chiffres sur la sortie standard. C’est illisible et inutilisable de toute façon. En plus c’est un peu bizarre, tu mélanges la sortie avec la logique de calcul, et tu affiches number deux fois dans ta fonction calculation, c’est un peu étrange. Tu passes number par valeur dans calculation donc number n’est pas mis à jour par l’appel, mais tu l’incrémentes (??) dans la boucle. Bref, la logique du code est à revoir.

+1 -0

Merci pour l’aide, pendant un moment l’application compilait mais continuait d’essayer de résoudre pour 3 sans y arriver après avoir changé les std::find en std::find_if, j’ai changé un peu le code pour ne plus afficher toutes les générations, seulement le nombre de départ et le nombre de générations, et j’ai changé la fonction calculation pour qu’elle fasse une copie du nombre entré pour ne pas faire une boucle infinie qui revient toujours à 1. J’ai fait le système de sauvegarde avec les connaissances que j’avais, sans rien chercher à part comment prendre un int d’un string, mais ce serait avec plaisir que j’écouterais des conseils pour l’améliorer.

Après les changements cités, le code ne compile plus, voici le code ainsi que les messages d’erreur:

#include <fstream>
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <string>
#include <cctype>

void calculation(int const a, std::string fileName) //makes the calculations by making a copy of 
{
    std::ofstream oFile(fileName, std::ios::app);

    int number{ a };

    int generation{ 0 };

    std::cout << "Number: " << number << ", generations: ";

    if (number % 2 == 0)
    {
        number /= 2;
        generation++;

        if (number == 1)
        {   
            std::cout << generation << std::endl;

            oFile << "Origin number: " << a << ", number of generation : " <<  generation << std::endl;
        }
    }

    else
    {
        number++;
        generation++;
    }
}

int main()
{
//recuperation of the last processed number
    
    std::ifstream iFile{"save.txt"};
    std::string line;
    std::vector<int> originNbr;
    std::vector<int> generationNbr;

    while (getline(iFile, line))
    {
    //finding the last origin nbr
        std::string strCurrentNbr;
        for (auto i{ std::find_if(std::begin(line), std::end(line), isdigit) }; isdigit(*i); i++)
        {
            if (isdigit(*i))
            {
                strCurrentNbr.push_back(*i);
            }
        }

        originNbr.push_back(std::stoi(strCurrentNbr));

        for (auto i{ std::find_if(std::rbegin(line), std::rend(line), isdigit)}; isdigit(*i); i++)
        {
            if (isdigit(*i))
            {
                strCurrentNbr.insert(0, 1, *i);
            }
        }

        generationNbr.push_back(std::stoi(strCurrentNbr));

    }

    std::ofstream oFile{ "save.txt", std::ios::app};
    
    int number;

    if (!(originNbr.empty()))
    {
        number = originNbr.back();
    }

    else
    {
        number = 1;

        oFile << "Origin number: " << number << ", number of generation : " << 1 << std::endl;

        std::cout << 1 << std::endl << std::endl;
    }

//calculation
    for (auto i{ number }; true; ++i)
    {
        if (i != 1)
        {
            calculation(i, "save.txt");
        }
    }

    return 0;
}

les messages d’erreur de l’output:

'Project1.exe' (Win32): Loaded 'C:\Users\debie\Documents\Programing\Project1\x64\Debug\Project1.exe'. Symbols loaded.
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\ntdll.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\kernel32.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\KernelBase.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\msvcp140d.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140d.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140_1d.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbased.dll'. 
The thread 0x7524 has exited with code 0 (0x0).
Debug Assertion Failed!

Program: ...rs\debie\Documents\Programing\Project1\x64\Debug\Project1.exe
File: C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\include\xstring
Line: 1898

Expression: cannot dereference string iterator because it is out of range (e.g. an end iterator)

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\kernel.appcore.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\msvcrt.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\rpcrt4.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\user32.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\win32u.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\gdi32.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\gdi32full.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\msvcp_win.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbase.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\imm32.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\TextShaping.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\uxtheme.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\combase.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\msctf.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\oleaut32.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\sechost.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\bcryptprimitives.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\TextInputFramework.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\CoreUIComponents.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\CoreMessaging.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\ntmarta.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\ws2_32.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\SHCore.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\WinTypes.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\advapi32.dll'. 
'Project1.exe' (Win32): Unloaded 'C:\Windows\System32\WinTypes.dll'
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\WinTypes.dll'. 
'Project1.exe' (Win32): Loaded 'C:\Windows\System32\ole32.dll'. 
The thread 0x685c has exited with code 3 (0x3).
The thread 0x7e28 has exited with code 3 (0x3).
The thread 0x76a8 has exited with code 3 (0x3).
The thread 0x3d74 has exited with code 3 (0x3).
The thread 0x7b3c has exited with code 3 (0x3).
The program '[9048] Project1.exe' has exited with code 3 (0x3).
Et un screenshot d'un message d'erreur
Et un screenshot d’un message d’erreur

Tu as plusieurs problèmes dans ton code (cf mon précédent message, les remarques en EDIT sont toujours globalement valides), et je pense que ça vient surtout du fait que tu essayes de tout coder à la fois au lieu de procéder par étapes plus simples. Là tu te retrouves avec un code qui est difficile à lire (et donc à comprendre et débugger) parce que tu essayes de coder le calcul de la séquence elle même en même temps que l’output et que la sauvegarde, et tu te retrouves avec deux fonctions qui font tout et rien en même temps. Perso je comprend pas ce qu’est censé faire calculation. Le nom est trop vague, et en lisant le contenu il semblerait qu’elle calcule le terme suivant du terme en entrée, et a une variable generation qui vaut toujours 1 à la fin (??) mais aucun mécanisme ne renvoie le prochain terme à l’appelant. Donc la fonction sert à rien.

À ta place je recommencerais complètement avec un objectif plus simple : coder une fonction qui prend juste un nombre en entrée et qui renvoie le prochain terme de la suite. Tu peux l’appeler dans main avec un argument littéral pour l’instant et afficher le résultat pour vérifier que c’est le bon. Une fois que tu as ça, tu peux écrire une autre fonction (qui se sert de la première) qui prend un nombre en entrée et qui renvoie le nombre d’itérations nécessaires pour atteindre 1. Puis une fois que tu as ça, tu peux travailler sur les IO de ton programme, sans toucher aux fonctions existantes.

+0 -0

Ca fonctionne.

J’ai réécrit mon code comme précisé, et maintenant le système de résolution du problème ET le système de sauvegarde fonctionne, je ne sais juste pas comment créer à proprement parler l’application, j’ai changé le debugger à release mais je ne connait pas la suite de la marche à suivre (j’utilise Visual Studio Community 2022).

Voici le code, dernière version (probablement):

#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>

void setSave(int lastValue) //saves
{
	std::ofstream saveFile{ "save.txt", std::ios::app };

	saveFile << lastValue << std::endl;
}

void setOutputValue(int value, int numberGen)
{
	std::ofstream outputFile{ "output.txt", std::ios::app };

	outputFile << value << ", " << numberGen << std::endl;
}

int getSave() //returns the last number used if it wasn't completely resolved, else returns the last number + 1
{
	int lastSavedNumber;

	std::ifstream saveGetFile{ "save.txt" }; //used to get the last used number
	std::ifstream verificationFile{ "output.txt" }; //used to verify if the program

	while (saveGetFile >> lastSavedNumber)
	{
		saveGetFile >> lastSavedNumber;
	}

	std::vector<int> saveNumVct, genVct;
	std::string numStr;

	while (std::getline(verificationFile, numStr)) //scans the entire file and puts the original value and the number of generation in the vectors
	{
		std::string currentNumStr = { std::begin(numStr), std::find_if(std::begin(numStr), std::end(numStr), isspace)-- };
		saveNumVct.push_back(std::stoi(currentNumStr));

		currentNumStr = { std::find_if(std::begin(numStr), std::end(numStr), isspace)++, std::end(numStr) };
		genVct.push_back(std::stoi(currentNumStr));
	}

	if (saveNumVct.size() == genVct.size())
	{
		return ++lastSavedNumber;
	}

	return lastSavedNumber;
}

int resolution1Gen(int number) //solves the problem for the next generation
{
	if (number % 2 == 0)
	{
		return number /= 2;
	}

	else
	{
		return (number *= 3) += 1;
	}
}

int resolutionEntireProblem(int number) //solves the entire problem using the previous fonction
{
	int copy{ number };
	int generation{ 0 };

	do
	{	
		++generation;

		copy = resolution1Gen(copy);
	} while (resolution1Gen(copy) != 1);

	return generation;
}

int main()
{
	int value = getSave();

	for (int i(value); true; i++)
	{
		setSave(i);

		std::cout << i << " went back to 1 after the " << resolutionEntireProblem(i) << "th generation" << std::endl;

		setOutputValue(i, resolutionEntireProblem(i));
	}

	return 0;
}

Voilà, merci énormément à vous, si vous avez des conseils pour rendre le code plus fonctionnel/optimisé/facile à lire, j’écouterai avec plaisir.

+0 -0

Salut,

int resolutionEntireProblem(int number) //solves the entire problem using the previous fonction
{
	int copy{ number };
	int generation{ 0 };

	do
	{	
		++generation;

		copy = resolution1Gen(copy);
	} while (resolution1Gen(copy) != 1);

	return generation;
}

Quelque remarques :

  • la variable copy est très mal nommée, et surtout elle ne sert à rien. Tu pourrais utiliser number directement ;
  • c’est dommage d’appeler resolution1Gen deux fois par tour de boucle mais ne retenir le résultat que d’un seul appel ;
  • la condition est dans le mauvais sens (en plus d’appeler resolution1Gen au lieu de comparer le résultat courant), si tu appelles la fonction avec 1 elle devrait renvoyer 0, pas 2. Si tu appelles avec 2, elle devrait renvoyer 1, pas 3. Et ainsi de suite, ta fonction ne renvoie jamais le bon résultat.
int main()
{
	int value = getSave();

	for (int i(value); true; i++)
	{
		setSave(i);

		std::cout << i << " went back to 1 after the " << resolutionEntireProblem(i) << "th generation" << std::endl;

		setOutputValue(i, resolutionEntireProblem(i));
	}

	return 0;
}

Quelque remarques en plus :

  • C’est sacrément dommage d’appeler resolutionEntireProblem deux fois pour chaque nombre au lieu de réutiliser le résultat !
  • Ta stratégie de sauvegarde sera complètement out of sync si quelqu’un lance ton appli, l’arrête, supprime save.txt puis la relance : output.txt aura des entrées en double.
  • save.txt est juste la liste des entiers, c’est pas très intéressant d’encombrer le disque avec ça (sans compter que ce sera de plus en plus lent à lire !). Si tu veux sauvegarder le dernier nombre, tu pourrais… avoir seulement le dernier nombre dans ton fichier.
+0 -0

J’ai renommé copy pour que son utilité soit plus apparente, et je l’utilise parce que sinon, le nombre entré est changé et redevient chaques fois 1.

#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>



void setOutputValue(int value, int numberGen)
{
	std::ofstream outputFile{ "output.txt", std::ios::app };

	outputFile << value << ", " << numberGen << std::endl;
}

int getSave() //returns the last number used if it wasn't completely resolved, else returns the last number + 1
{
	std::ifstream saveGetFile{ "output.txt" }; //used to get the last used number and verify if the program completed the problem for this number

	std::vector<int> saveNumVct, genVct;
	std::string numStr;

	while (std::getline(saveGetFile, numStr)) //scans the entire file and puts the original value and the number of generation in the vectors
	{
		std::string currentNumStr = { std::begin(numStr), std::find_if(std::begin(numStr), std::end(numStr), isspace)-- };
		saveNumVct.push_back(std::stoi(currentNumStr));

		currentNumStr = { std::find_if(std::begin(numStr), std::end(numStr), isspace)++, std::end(numStr) };
		genVct.push_back(std::stoi(currentNumStr));
	}

	if (saveNumVct.size() == 0)
	{
		return 1;
	}

	if (saveNumVct.size() == genVct.size())
	{
		return (saveNumVct.back())+=1;
	}

	return saveNumVct.back();
}

int resolution1Gen(int number) //solves the problem for the next generation
{
	if (number % 2 == 0)
	{
		return number /= 2;
	}

	else
	{
		return (number *= 3) += 1;
	}
}

int resolutionEntireProblem(int const number) //solves the entire problem using the previous fonction
{
	int usableNum{ number }; //used so the entered number isn't changed 
	int generation{ 0 };

	while (usableNum != 1)
	{	
		++generation;

		usableNum = resolution1Gen(usableNum);
	} 

	return generation;
}

int main()
{
	int value = getSave();

	for (int i(value); true; i++)
	{
		auto generation = resolutionEntireProblem(i);

		std::cout << i << " went back to 1 after the " << generation;
		
		if (generation % 10 == 1)
		{
			std::cout << "st ";
		}

		else if (generation % 10 == 2)
		{
			std::cout << "nd ";
		}

		else if (generation % 10 == 3)
		{
			std::cout << "rd ";
		}

		else
		{
			std::cout << "th ";
		}

		std::cout << "generation." << std::endl;

		setOutputValue(i,generation);
	}

	return 0;
}

Voilà, y a-t-il encore quoi que ce soit que je puisse améliorer?

+0 -0

J’ai renommé copy pour que son utilité soit plus apparente, et je l’utilise parce que sinon, le nombre entré est changé et redevient chaques fois 1.

Tu confonds passage par valeur et passage par référence. Si ta fonction prend int number en argument, elle reçoit déjà une copie de number que tu peux modifier sans que la variable passée par l’appelant soit modifiée. Ta variable usableNum est inutile dans resolutionEntireProblem.

int resolution1Gen(int number) //solves the problem for the next generation
{
	if (number % 2 == 0)
	{
		return number /= 2;
	}

	else
	{
		return (number *= 3) += 1;
	}
}

Là aussi il semblerait que ne comprends pas bien les histoires de passer par référence vs par valeur et ce que fait return. Tu n’as pas besoin de faire des assignements dans ton return, parce que tu ne modifies que la valeur de la copie locale à la fonction en faisant ça (ce qui ne te sert à rien). Tu peux réécrire cette fonction de la manière suivante

int resolution1Gen(int number) {
    if (number % 2 == 0) {
        return number / 2;
    } else {
        return number * 3 + 1;
    }
}

et elle se comportera exactement pareil.

+0 -0

En effet, si tu passes une référence alors la fonction reçoit effectivement l’adresse du nombre que l’appelant a passé à la fonction et peut le modifier de cette façon.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte