Gå til innhold

Bone's Offisielle C++ hjelpetråd


Anbefalte innlegg

Du vil vel gjerne kalle sum<T, P> (eks. sum<double, double>) istedetfor bare sum.

Skal den ikke kunne ta de typene ut ifra sammenhengen?

 

Men hva om han kaller sum(5)? Hvilken av sum-funksjonene skal kalles? Begge er mulige. Dessuten, hva slags datatype skal brukes? int? long? short? byte? Umulig å bestemme utifra kallet uten en hel masse eksplisitte regler for hvordan slike tvetydigheter skal behandles. Noe som iallefall JEG ville styrt unna, om jeg skulle designet språket.

 

Basert på dette så stiller jeg meg tvilsom til det. :)

Endret av hallgeirl
Lenke til kommentar
Videoannonse
Annonse

Var visst ikke så enkelt. stackoverflow.com har dog svaret: http://stackoverflow.com/questions/3744400/decltype-with-a-variadic-template-function

 

#include <iostream>
#include <type_traits>
using namespace std;

template <class T>
typename add_rvalue_reference<T>::type val();

template <class T>
struct id
{
  typedef T type;
};

template <class T, class... P>
struct sum_type;

template <class T>
struct sum_type<T> : id<T> {};

template <class T, class U, class... P>
struct sum_type<T, U, P...>
: sum_type< decltype(val<const T&>() + val<const U&>()), P... > {};


template <class T>
T sum(const T& in)
{
  return in;
}

template <class T, class... P>
typename sum_type<T, P...>::type sum(const T& t, const P&... p)
{
  return t + sum(p...);
}

int main()
{
  cout << sum(5, 10.0, 22.2) << endl;
}

 

Hvem sa at template metaprogrammering ikke var kult? >_<

Endret av TheMaister
Lenke til kommentar

Høhø, la til rvalue semantics i tillegg.

 

#include <iostream>
#include <type_traits>
using namespace std;

template <class T>
typename add_rvalue_reference<T>::type val();

template <class T>
struct id
{
  typedef T type;
};

template <class T, class... P>
struct sum_type;

template <class T>
struct sum_type<T> : id<T> {};

template <class T, class U, class... P>
struct sum_type<T, U, P...>
: sum_type< decltype(val<const T&>() + val<const U&>()), P... > {};


template <class T>
T sum(const T& in)
{
  return in;
}

template <class T>
typename remove_reference<T>::type&& sum(T&& in)
{
  return move(in);
}

template <class T, class... P>
typename sum_type<T, P...>::type sum(const T& t, const P&... p)
{
  return t + sum(p...);
}

template <class T, class... P>
typename sum_type<T, P...>::type sum(T&& t, P&&... p)
{
  return move(t) + sum(p...);
}

int main()
{
  cout << sum(5, 10.0, 22.2, 30) << endl;
}

 

Nå ser det da veldig vakkert ut ... xD Hm :v Virker ikke som den gjør ting helt riktig, men, men :)

Endret av TheMaister
Lenke til kommentar

Hvordan ville tilsvarende kode sett ut i e.g. C# eller Java egentlig? (Hvis det går)

 

Jeg har ikke helt fått med meg det du nevnte om å også returnere type i tillegg til sum, men i Common Lisp kan en summere "hva som helst" slik:

 

CL-USER> (defun sum (&rest args)
          (apply #'+ args))
SUM
CL-USER> (sum 5 10.0 22.2 30)
67.2
CL-USER> (sum 1/2 3/4)
5/4
CL-USER> (sum #C(5 -3) #C(5/3 7)) ;; komplekse tall
#C(20/3 4)
CL-USER> 

 

 

edit:

De som kjenner til dette fra før vet sikkert at det å lage en SUM funksjon som dette har liten hensikt da funksjonen + allerede aksepterer et vilkårlig antall argumenter (&REST).

Endret av worseisworser
Lenke til kommentar

En kan ikke bruke pluss, minus gange eller dele i generics i C#. Så

 

public T Sum<T>(T a, T b)
{
 return a + b;
}

Er ulovlig. Men C# tar datatypen ut av sammenhengen, så hvis du skriver for eksempel slik:

 

public T Sum<T>(T a, T b, params T rest) // Definer en funksjon som tar inn minst to tall
{
 decimal total;
 total = (decimal)a + (decimal)b;
 foreach(var item in rest)
   total += (decimal)item;
 return (T)total;
}
public void Main()
{
 var dec = Sum(1, 2, 3, 7); // var er det samme som auto i C++0x etter det jeg har forstått av auto
 // dette burde returnere et integer.
}

en må bruke decimal eller en annen datatype, ettersom + er udefinert på T. Det hadde vært kjekt om en kunne lagt på en "Arithmetic" constraint eller noe, men det går altså ikke. Generics er mer egnet for collections og delegates, og ikke så mye for beregning.

 

edit: glemte at C# er dynamisk nå, så nå kan man lage noe slikt som fungerer.

 

public dynamic Sum(dymaic a, dynamic b, params dynamic values)
{
 dynamic res = a + b;
 foreach(var item in values)
   res += item;
 return res;
}

 

dynamic har selvsagt ulempen av å være run-time sjekket, og er derfor noe tregere enn generics. Jeg vet ikke hva utbyttet av dynamic er kontra å bruke decimal med tanke på ytelse, siden decimal er software 128-bit heltall (kanskje hardware, med tanke på prosessor-utvidelser, men jeg har ikke sjekket)

Endret av GeirGrusom
Lenke til kommentar

Ja, klart. Dette er bare et forsøk på å pushe syntaksen i et statisk typed språk som C++. ;) Så vidt jeg har skjønt er lisp dynamisk typed, og da finnes ikke problemstillingen.

 

Mh, vel det er vel en slags hybrid av slag(#1); "normalen" er dynamisk eller sterk typing -- og compile-time (og inferred; "type inferrence") type-informasjon brukes i større grad til optimalisering i så måte at generert kode som ellers ville gjort run-time type-sjekking vil kunne fjernes om en ønsker det.

 

 

Denne gjør et kall til en intern funksjon, GENERIC-+, som altså håndterer "hva som helst" og gjør run-time type-sjekking:

 

; SLIME 2010-09-18
CL-USER> (defun test (x y)
          (+ x y))
TEST
CL-USER> (disassemble 'test)
; disassembly for TEST
; 03F3F1DF:       4C8D1C25E0010020 LEA R11, [#x200001E0]      ; GENERIC-+
                                                             ; no-arg-parsing entry point
;       E7:       41FFD3           CALL R11
;       EA:       480F42E3         CMOVB RSP, RBX
;       EE:       488BE5           MOV RSP, RBP
;       F1:       F8               CLC
;       F2:       5D               POP RBP
;       F3:       C3               RET
NIL
CL-USER>

 

 

..mens denne genererer "inline" kode som summerer, kun, FIXNUMs (samme bakenforliggende eller maskinvaremessige størrelse som int i C) uten å gjøre run-time type-sjekking i det hele tatt:

 

CL-USER> (defun test (x y)
          (declare (fixnum x y))
          (the fixnum (+ x y)))
STYLE-WARNING: redefining COMMON-LISP-USER::TEST in DEFUN
TEST
CL-USER> (disassemble 'test)
; disassembly for TEST
; 03FB2D1F:       4801FA           ADD RDX, RDI               ; no-arg-parsing entry point
;       22:       488BE5           MOV RSP, RBP
;       25:       F8               CLC
;       26:       5D               POP RBP
;       27:       C3               RET
NIL
CL-USER> 

 

#1: ANSI-standaren er rimelig åpen eller fri m.t.p. hvordan en implementasjon skal være her.

Endret av worseisworser
Lenke til kommentar

Hei programmerere,

 

Står litt fast her, så trenger litt hjelp. Skal forklare hva som skjer når jeg kjører kodesnutten i main.cpp. Forstår deler av det. Hvorfor blir kopikonstruktøren kallet 4 ganger? Hvorfor blir to av dem fjernet av destruktøren med en gang, mens de to andre blir fjernet etter {} ?

 

Takk på forhånd!

 

vektor.h

 

//---------------------------------------------------------------------------
#ifndef VektorH
#define VektorH
//---------------------------------------------------------------------------
class Vektor{
public:
       Vektor();
       Vektor(int start, int antal);
       Vektor(const Vektor& org);
       ~Vektor();

       //operatorar
       Vektor operator+(const Vektor& v) const;
       const Vektor& operator=(const Vektor& v);

       // metodar
       Vektor metode1(Vektor v);
       Vektor metode2(Vektor& v);
       Vektor& metode3(Vektor v);
       Vektor& metode4(Vektor& v);
       Vektor* metode5(Vektor& v);

private:
       const int i1;
       int ant;
       int *p;
       int nr;
       static int neste;
};

#endif

 

vektor.cpp

#include "Vektor.h"
#include <iostream>
using namespace std;
//---------------------------------------------------------------------------
int Vektor::neste = 1;  // gir verdi til statisk variabel

Vektor::Vektor(): ant(0), i1(1), p(0){
       nr= neste++;
       cout << "Standard konstruktør. Nr: "  << nr << endl;


}

Vektor::Vektor(int start, int antal): i1(start), ant(antal) {
       nr= neste++;
       cout << "Spesial konstruktør. Nr: " << nr << endl;
       p = new int[ant];
}

Vektor::Vektor(const Vektor& org): i1(org.i1), ant(org.ant){
       nr= neste++;
       cout << "Kopikonstruktør. Nr: " << nr << endl;
       p = new int[ant];
       for (int i = 0; i < ant; i++){
               p[i] = org.p[i];
       }
}

Vektor::~Vektor(){
       cout << "Destruktør. Nr: " << nr << endl;
       delete[] p;
}

//operatorar
Vektor Vektor::operator+(const Vektor& v) const{
       cout << "Operator+" << endl;
       Vektor temp(*this);
       for (int i = 0; i < ant; i++){
               temp.p[i] += v.p[i];
       }
       return temp;
}

const Vektor& Vektor::operator=(const Vektor& v){
       cout << "Tildelingsoperator" << endl;
       if (this != &v){
               delete[] p;
               ant = v.ant;
               p = new int[ant];
               for (int i = 0; i < ant; i++){
                       p[i] = v.p[i];
               }
       }
       return *this;
}


Vektor Vektor::metode1(Vektor v){
       return v;
}

Vektor Vektor::metode2(Vektor& v){
       return v;
}


/* Vektor& Vektor::metode3(Vektor v){
       return v;
} */


Vektor& Vektor::metode4(Vektor& v){
       return v;
}

Vektor* Vektor::metode5(Vektor& v){
       Vektor *p = new Vektor(v);
       return p;
}

 

main.cpp

#include <iostream>
#include "vektor.h"

using std::cout;
using std::endl;

void main()
{
{
	Vektor a(1,5), b(1,5), c(1,5);

	a = b + c + b;
}

cout << "Slutt på programmet." << endl;
system("PAUSE");
}

 

Console

Spesial konstruktør. Nr: 1
Spesial konstruktør. Nr: 2
Spesial konstruktør. Nr: 3
Operator+
Kopikonstruktør. Nr: 4
Kopikonstruktør. Nr: 5
Destruktør. Nr: 4
Operator+
Kopikonstruktør. Nr: 6
Kopikonstruktør. Nr: 7
Destruktør. Nr: 6
Tildelingsoperator
Destruktør. Nr: 7
Destruktør. Nr: 5
Destruktør. Nr: 3
Destruktør. Nr: 2
Destruktør. Nr: 1
Slutt på programmet.
Press any key to continue . . .

Lenke til kommentar

Står litt fast her, så trenger litt hjelp. Skal forklare hva som skjer når jeg kjører kodesnutten i main.cpp. Forstår deler av det. Hvorfor blir kopikonstruktøren kallet 4 ganger?

 

Høyst sannsynlig fordi kompilatoren er litt slapp på å optimalisere vekk objektene som returneres.

 

Hvorfor blir to av dem fjernet av destruktøren med en gang, mens de to andre blir fjernet etter {} ?

 

Etter? ITYM "før" (det er faktisk et krav).

 

Vektor Vektor::operator+(const Vektor& v) const{
       cout << "Operator+" << endl;
       Vektor temp(*this);
       for (int i = 0; i < ant; i++){
               temp.p[i] += v.p[i];
       }
       return temp;
}

 

Dette er den interessante delen 1 -- det er to kall på copy ctor her i den naive implementasjonen: en for å lage temp, og en for å lage returverdien.

 

{
	Vektor a(1,5), b(1,5), c(1,5);

	a = b + c + b;
}

 

Alle copy ctors blir kalt i det aritmetiske uttrykket. Koden er ekvivalent med:

 

Vektor tmp1(operator+(b, c)); // 2 copy ctors (1 for temp, 1 for tmp1)
Vektor tmp2(operator+(tmp1, b)); // 2 copy ctors (1 for temp, 1 for tmp2)
operator=(a, tmp2);

 

Langt i fra alle kompilatorer gjør 4 copy ctor kall (icc og gcc gjør ikke det, clang++ gjør det, iallfall blant de jeg kan sjekke kvikt).

 

edit: trykkfeil

Endret av zotbar1234
Lenke til kommentar

hei, driver å fikler litt med opengl igjen og prøver å lage en liten kodesnutt for å rotere via tastaturet, kompilerer helt fint, men det skjer ikke noe når jeg trykker. liten snutt her, kan paste hele koden viss det hjelper.

 

void handleKeyPress (unsigned char key, 
                 int x, int y) { //mouse pos
switch (key) {
case 27: //escape key
	exit(0);
}

switch (key) {
case 65:// tror dette er A

_rotate+=1.0f;
};
}

Lenke til kommentar

hei, driver å fikler litt med opengl igjen og prøver å lage en liten kodesnutt for å rotere via tastaturet, kompilerer helt fint, men det skjer ikke noe når jeg trykker. liten snutt her, kan paste hele koden viss det hjelper.

 

void handleKeyPress (unsigned char key, 
                 int x, int y) { //mouse pos
switch (key) {
case 27: //escape key
	exit(0);
}

switch (key) {
case 65:// tror dette er A

_rotate+=1.0f;
};
}

 

Jeg regner med det er Glut du bruker for input (i og med at du bruker OpenGL og du ikke nevner noe annet)? Isåfall må keypress-funksjonen din registreres som en callback-funksjon ved hjelp av funksjonen glutKeyboardFunc ( http://www.opengl.org/resources/libraries/glut/spec3/node49.html#SECTION00084000000000000000 ).

 

Forresten, hvorfor har du 2 switch-er? Skal du ha en switch per condition er jo hele poenget borte vekk :)

switch(blabla){
case 5:
 foo();
 break;
case 6:
 bar();
 break;
default:
 applejuice();
}

Endret av hallgeirl
Lenke til kommentar

Hvis jeg lager en pointer i main, og gir den til 2 forskjellige klasser.

Og så vil slette pointeren ifra en av klassene, slik at den blir slettet hos begge. Hvordan går jeg fram da?

 

Hvis jeg sletter den i den ene klassen, blir det seg fault når jeg prøver å slette den samme i andre klassen.

Endret av Ståle
Lenke til kommentar

Hvis jeg lager en pointer i main, og gir den til 2 forskjellige klasser.

Og så vil slette pointeren ifra en av klassene, slik at den blir slettet hos begge. Hvordan går jeg fram da?

 

Kan du lage den minste kodesnutten som illustrerer problemet?

 

Hvis jeg sletter den i den ene klassen, blir det seg fault når jeg prøver å slette den samme i andre klassen

 

Hvis du allokerer et objekt dynamisk vha new/new[]/malloc, så skal det objektet slettes nøyaktig en gang vha delete/delete[]/free.

Lenke til kommentar

class A {
Asd* internPtr;
settInternPtr( Asd* );
}

A* enA = new A();
A* toA = new A();

Asd* test = new Asd();

enA->settInternPtr( test );
toA->settInternPtr( test );

 

 

Det blir ihvertfall feil når jeg gjør sånn, men funker hvis jeg lager en kopi og sender til #2.

Endret av Ståle
Lenke til kommentar

class A {
Asd* internPtr;
settInternPtr( Asd* );
}

A* enA = new A();
A* toA = new A();

Asd* test = new Asd();

enA->settInternPtr( test );
toA->settInternPtr( test );

 

Når du gjør en "delete internPtr;" i en metode kalt for enA eller toA, så _er_ det objektet slettet. Du trenger ikke å slette det en gang til. Det du kanskje er interessert i er hvordan man kan si "delete internPtr;" i enA/toA uten å bry seg om den andre har allerede foretatt en sletting? Dersom det test peker på ikke er sirkulært er den enkleste løsningen boost::shared_ptr.

Endret av zotbar1234
  • Liker 1
Lenke til kommentar
  • 4 uker senere...

Leker meg litt med C++. Jeg har brukt http://www.cprogramming.com/tutorial.html#c++tutorial og nettopp kommet forbi arrays, men lurte litt på et program jeg lagde tidligere. Hvis du velger noe annet enn 'bool' går den helt bananas, hvordan kan jeg løse dette på en bedre måte?

 

#include <iostream>

using namespace std;

     int main()
{
for (;{
   bool unit;
   double degree;
   cout<<"Starting unit: Celsius(0) or Fahrenheit(1)?\n";
   cin>> unit;
   cin.ignore();
   if (unit==true) {
       cout<<"You picked Fahrenheit\nAdd degrees: ";
       cin>> degree;
       cin.ignore();
       cout<< (degree-32)*5/9 <<" Celsius";
       }
   else {
          cout<<"You picked Celsius\nAdd degrees: ";
       cin>> degree;
       cin.ignore();
       cout<< degree*9/5+32 <<" Fahrenheit";
         }
cin.get();
     }
}

 

Som du kanskje ser så prøvde jeg ut 'for'. Legger ved en annen kodesnutt i slengen som har samme problem (hvis du velger noe annet enn 'int').

 

#include <iostream>

using namespace std;

int main()
{
 int input;

 do {

 cout<<"1. Door one\n";
 cout<<"2. Door two\n";
 cout<<"3. Door three\n";
 cout<<"4. Exit\n";
 cout<<"Selection: ";
 cin>> input;
 switch ( input ) {
 case 1:            
   cout<<"\nOne\n\n";
   break;
 case 2:            
   cout<<"\nTwo\n\n";
   break;
 case 3:            
   cout<<"\nThree\n\n";
   break;
 case 4:            
   cout<<"\nThank you for playing\n\n";
   break;
 default:            
   cout<<"\nError, bad input\n\n";
   break;

 } 
 }while (input!=4);

}

 

Kom gjerne med konstruktiv kritikk, jeg har aldri kodet før så hvis det er mye tull; skrik ut.

Lenke til kommentar
  • 1 måned senere...

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...