[C++] voorkomen C2556, functions differ only by return type

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Beste Tweakers,

Ik heb een matrix klasse overgeerft. Hierin wil ik een functie toevoegen die handig is in het geval een matrixmultiplicatie een scalar opleverd, maar ik bots tegen C2556 aan.

Op het moment heb ik dit:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
template <class Type> 
class CMatrix
{

public:
// snip

  // arithmetic operations with matrix M
  virtual CMatrix<Type> operator * (const CMatrix<Type>& M) const;
  // arithmetic operations with matrix M, producing a scalar
  virtual Type operator * (const CMatrix<Type>& M) const; // deze is dus nieuw
}


Waarop ik dus de fout "error C2556: 'int CMatrix<Type>::operator *(const CMatrix<Type> &) const' : overloaded function differs only by return type from 'CMatrix<Type> CMatrix<Type>::operator *(const CMatrix<Type> &) const'" krijg. Ik heb het nu tijdelijk opgelost door "^" voor mijn nieuwe operator te gebruiken, maar dat is natuurlijk vies.

Is er een elegante manier om dit op te lossen? Op google kom ik alleen beginnersvragen tegen. Omdat dit een operator betreft kan ik het helaas niet met een dummy argument oplossen.

Bedankt voor jullie suggesties!

Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Je zou een cast operator aan een matrix toe kunnen voegen die kijkt of hij 1x1 is en dan cast naar scalar, anders een error. (kan je ook nog naar kolom en rijvectoren casten)

[ Voor 20% gewijzigd door Zoijar op 22-06-2010 10:07 ]


Acties:
  • 0 Henk 'm!

  • Korben
  • Registratie: Januari 2001
  • Laatst online: 13-07 01:53

Korben

() => {};

Op zich nogal logisch dat je die melding krijgt, je hebt zoals de melding zegt twee functies met dezelfde argumenten en alleen verschillende return-types; dit kan gewoon niet, want de compiler kan niet meer resolven welke van de twee jij bedoelt.

.oisyn: Échte programmeurs haten PHP met een passie. Ben jij soms geen echte programmeur?


Acties:
  • 0 Henk 'm!

  • HuHu
  • Registratie: Maart 2005
  • Niet online
In welk geval levert een matrix multiplicatie een scalar op dan? Een multiplicatie tussen een m×n en n×p matrix levert een m×p matrix op en een multiplicatie tussen een m×n en een scalar levert een m×n matrix op.

Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Vermoedelijk is de aanname dat een 1×[i]1 matrix gelijk staat aan een scalar.

Het onderliggende probleem is dat er twee template argumenten ontbreken in de Matrix template; de correcte definitie is
C++:
1
2
3
4
5
template <class Type, unsigned M, unsigned N>  
class CMatrix 
{ 
   // ...
};


Vervolgens kun je in de partiële specialisatie van CMatrix<CType, 1, 1> een operator CType() toevoegen.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

MSalters schreef op dinsdag 22 juni 2010 @ 11:09:
Het onderliggende probleem is dat er twee template argumenten ontbreken in de Matrix template; de correcte definitie is
Hoewel dat een mogelijke oplossing is, is het natuurlijk nonsens dat jouw code correct is en die van de TS niet. Wellicht wil de TS wel gewoon een dynamische Matrix klasse, en de grootte dus niet compile-time vastleggen.

Een andere oplossing is om een proxy klasse terug te geven die converteerbaar is naar zowel een Matrix<Type> als een Type. In het geval dat het niet om een 1x1 matrix gaat zou je dan tijdens de conversie een runtime error kunnen geven.

Zoiets dus:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template<class Type>
class CMatrixMulResult
{
public:
    CMatrixMulResult(CMatrix<Type> result) : m_result(result) { }

    operator const CMatrix<Type> &() const
    {
        return m_result;
    }

    operator Type() const
    {
        assert(m_result.Width() == 1 && m_result.Height() == 1);
        return m_result(0, 0); // of wat voor manier dan ook om een element op te vragen
    }

private:
    CMatrix<Type> m_result;
};

In je operator* return je dan altijd een CMatrixMulResult<Type>, die assignbaar is aan zowel een CMatrix<Type> als een Type.

[ Voor 48% gewijzigd door .oisyn op 22-06-2010 11:27 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Bedank voor de reacties zover!

Een 1xM * Mx1 operatie levert een scalar op. Die zit inderdaad in een 1x1 instantie van mijn matrix klasse, maar ik heb hem nodig in het onderliggende datatype. Het is inderdaad logisch dat de compiler hier op vastloopt (hoewel, als ik de uitkomst aan een double assign, niet aan een CMatrix<double>, dan moet de compiler toch eigenlijk prima weten welke van de twee hij hebben moet?).

Mijn vraag is dus inderdaad hoe dit netjes op te lossen.

De cast operator klinkt als een goed idee, eens proberen!

@ MSalters: ik wil eigenlijk zo weinig mogelijk werk aan die klasse verzetten, hij doet zijn werk prima, ookal zitten er wat kleine dingen in die netter hadden gekunt. Ik vraag me af voor jouw suggestie, hoe ik dan de grootte van mij matrix zou veranderen. Ik kan *this toch niet omcasten van e.g. CMatrix<double,3,4> naar CMatrix<double,3,2>?

@ .Oisyn: bedank voor die suggestie. Erg net (kun je makkelijk allerlei spanndende truucs doen voordat je returned). Als ik de code goed begrijp (de references in het geval van returnen CMatrix), heeft het ook geen overhead, netjes! Ik ga eerst de cast operatie proberen omdat het het minst ingrijpend is, maar hier moet ik eens naar kijken als ik deze klasse meer onder handen wil nemen.

[ Voor 18% gewijzigd door TheWickedD op 22-06-2010 11:33 . Reden: .oisyn's reactie ]


Acties:
  • 0 Henk 'm!

  • HuHu
  • Registratie: Maart 2005
  • Niet online
TheWickedD schreef op dinsdag 22 juni 2010 @ 11:27:

Een 1xM * Mx1 operatie levert een scalar op. Die zit inderdaad in een 1x1 instantie van mijn matrix klasse, maar ik heb hem nodig in het onderliggende datatype. Het is inderdaad logisch dat de compiler hier op vastloopt (hoewel, als ik de uitkomst aan een double assign, niet aan een CMatrix<double>, dan moet de compiler toch eigenlijk prima weten welke van de twee hij hebben moet?).

Mijn vraag is dus inderdaad hoe dit netjes op te lossen.
Nee, een multiplicatie tussen een 1×m en m×1 matrix levert een 1×1 matrix op en geen scalar.

Ook kan de compiler niet automatisch kiezen:

C++:
1
2
3
4
5
6
7
CMatrix m1bij5;
CMatrix m5bij1;
CMatrix m5bij3;

CMatrix m1bij3 = m1bij5 * m5bij3; // dit kan
double scalar1 = m1bij5 * m5bij1; // dit wil je
double scalar2 = m1bij5 * m5bij3; // dit kan, maar toch ook niet ???


Wat moet er gekozen worden in het laatste geval?

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

HuHu schreef op dinsdag 22 juni 2010 @ 11:41:
[...]

Nee, een multiplicatie tussen een 1×m en m×1 matrix levert een 1×1 matrix op en geen scalar.
In veel contexten is een scalar equivalent aan een 1-vector of 1×1 matrix.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
@ .oisyn: inderdaad, voor mij is de uitkomst een scalar (een lengte^2: vector*vector.transpose).
HuHu schreef op dinsdag 22 juni 2010 @ 11:41:
Ook kan de compiler niet automatisch kiezen:

C++:
1
2
3
4
5
6
7
CMatrix m1bij5;
CMatrix m5bij1;
CMatrix m5bij3;

CMatrix m1bij3 = m1bij5 * m5bij3; // dit kan
double scalar1 = m1bij5 * m5bij1; // dit wil je
double scalar2 = m1bij5 * m5bij3; // dit kan, maar toch ook niet ???


Wat moet er gekozen worden in het laatste geval?
In de laatste twee gevallen moet mijn multiplication operator die een double teruggeeft aangeroepen worden, in het laatste geval zou die een runtime error throwen (da's beter dan iets idioots teruggeven, dat leidt tot veel naardere bugs).

PS. sorry voor mijn wat crappy Nederlands, merk dat ik nu al zolang geen Nederlands meer heb gesproken dat mijn woordkeus en grammatica niet erg vloeiend zijn.

Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Wat is er mis met
C++:
1
2
3
4
5
6
7
8
template<CType>
CMatrix
{
  public:
    CType ToScalar() const { assert(GetRows() == 1); assert(GetColumns() == 1); return Get(0,0); }
}

double scalar = (m1bij5 * m5bij1).ToScalar();

Het soort impliciete conversies die je doet maken je code er niet leesbaarder op. Je doet iets speciaal, namelijk een scalar uit een matrix extraheren, laat het dan ook opvallen.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Als een 1×1 matrix mathematisch equivalent is aan een scalar, waarom is een impliciete conversie dan niet gewenst?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
.oisyn schreef op dinsdag 22 juni 2010 @ 12:45:
Als een 1×1 matrix mathematisch equivalent is aan een scalar, waarom is een impliciete conversie dan niet gewenst?
Ik ga hier persoonlijk in mee met .oisyn. Waar nodig (bijv. omdat de operatie ook geldig is met een matrix) kan ik altijd nog een expliciete conversie doen, e.g. double(CMatrix<double>)

Ik heb meteen ook even een conversion operator naar vector geschreven, handig!

Omdat het redelijk wat uitzoekwerk was, hieronder the implementatie (btw, zoek voor "conversion operator", zo heten die cast operators officieel blijkbaar):
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// .h:
#include <vector>

template <class Type> 
class CMatrix
{
// snip
    // conversion operator
    operator             Type () const; // if matrix is scalar, extracts the scalar
    operator std::vector<Type>() const; // if matrix is vector, extracts the vector
}

//.cpp
template <class Type>
CMatrix<Type>::operator Type() const
{
    if (Rows()==1 && Cols()==1)
    {
        return (*this)(0);
    }
    else
    {
        return Type(0); // en throw etc
    }
}

template <class Type>
CMatrix<Type>::operator std::vector<Type>() const
{
    if (Rows()==1 || Cols()==1)
    {
        return std::vector<Type>(&(*this)(0),(&(*this)(0))+Len());
    }
    else
    {
        return std::vector<Type>(); // en throw etc
    }
}


Bedankt!

Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

.oisyn schreef op dinsdag 22 juni 2010 @ 12:45:
Als een 1×1 matrix mathematisch equivalent is aan een scalar, waarom is een impliciete conversie dan niet gewenst?
Omdat zijn matrix klasse in de huidige vorm niet kan garanderen dat ie 1x1 is.
Als zijn code
C++:
1
2
3
4
5
6
7
8
9
10
11
template <class Type, int columns, int rows>
class CMatrix
{
};

template <class Type>
class CMatrix<Type, 1, 1>
{
public:
  operator Type() { return Get(0, 0); }
}

zo was dan ben ik het met je eens.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 16:28
H!GHGuY schreef op dinsdag 22 juni 2010 @ 19:24:
Omdat zijn matrix klasse in de huidige vorm niet kan garanderen dat ie 1x1 is.
Tuurlijk wel. Alleen niet at compiletime. Van een std::vector() kun je ook niet at compiletime garanderen dat front() een element kan opleveren (als de vector leeg is) maar dat betekent niet dat die methode niet nuttig is.

Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Soultaker schreef op dinsdag 22 juni 2010 @ 20:35:
[...]

Tuurlijk wel. Alleen niet at compiletime. Van een std::vector() kun je ook niet at compiletime garanderen dat front() een element kan opleveren (als de vector leeg is) maar dat betekent niet dat die methode niet nuttig is.
Juist, maar dat is een expliciete function call. Het gaat hier over impliciete calls/conversies.
Ik moet je wel nageven dat dit al meer neigt naar 'maintainability/coding style' dan echt mathematische correctheid.

Maintainability dicteert dat je alles wat niet triviaal is expliciet in de code tot expressie brengt. Ik vind de impliciete conversie/assignment van een (op het eerste zicht) willekeurige matrix aan een double niet triviaal, tenzij uit de context, zoals de klassedefinitie, duidelijk is dat het een 1x1 matrix is.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <typename Type>
Type DoSomething(CMatrix<Type> a, CMatrix<Type> b)
{
  return (a*b)
}
// tov
template<typename Type, int rows, int columns>
Type DoSomething(CMatrix<Type, 1, columns> a, CMatrix<Type, rows, 1> b)
{
  return (a*b)
}

template <typename Type>
Type DoSomething(CMatrix<Type> a, CMatrix<Type> b)
{
  return (a*b).ToScalar()
}


Beide zijn mathematisch correct, maar ik zou persoonlijk veel liever de 2de/3de code onderhouden dan de eerste.
Het eerste groentje die zijn handen op de de eerste functie legt heeft bijna gegarandeerd een crash/assert aan zijn been in de impliciete conversie. Ikzelf ben nooit met matrices bezig en reken mezelf dus tot deze categorie mocht ik deze functies als lib krijgen :+

Hoe vroeger je errors detecteert hoe minder ze je kosten. Compile-time is typisch vroeger dan run-time dus heb je potentieel de aandeelhouders een plezier gedaan (en jezelf misschien ook).

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 02-04-2024
Interessante discussie, zit wel wat in denk ik. Past ook beter in mijn stijl, ik ga bijna altijd voor de duidelijkheid. Maar als je dan toch voor duidelijkheid en flexibiliteit gaat, dan is de oplossing die .oisyn bovenaan aanreikt misschien nog wel beter, maar dat is echt voor de toekomst voor mij.

Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

TheWickedD schreef op woensdag 23 juni 2010 @ 02:44:
Interessante discussie, zit wel wat in denk ik. Past ook beter in mijn stijl, ik ga bijna altijd voor de duidelijkheid. Maar als je dan toch voor duidelijkheid en flexibiliteit gaat, dan is de oplossing die .oisyn bovenaan aanreikt misschien nog wel beter, maar dat is echt voor de toekomst voor mij.
De oplossing van .oisyn, hoe geniaal ook, levert nog steeds slechts fouten @runtime.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Sowieso zou ik zelf niet kiezen voor mijn oplossing, ik droeg alleen maar een extra mogelijkheid aan :). Mijn oplossing is suf omdat alleen een vermenigvuldiging naar een scalar te converteren is, maar een algemene 1x1 matrix niet. Maar uit de vermenigvuldiging komt (mogelijk) gewoon een 1x1 matrix. Mijn oplossing is dus onnodig te specifiek.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Big Womly
  • Registratie: Oktober 2007
  • Laatst online: 01-09 13:39

Big Womly

Live forever, or die trying

Vermenigvuldigen van 2 matrixen levert in de algemen regel een nieuwe matrix op. Laat je * operator dan ook een 1x1 matrix teruggeven, en de cast naar een scalair is dan maar voor de klasse die gebruik maakt van je matrix klasse, of voorzie je klasse van een isScalar en/of getScalar functie

[ Voor 12% gewijzigd door Big Womly op 23-06-2010 12:58 ]

When you talk to God it's called prayer, but when God talks to you it's called schizophrenia


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
.oisyn schreef op dinsdag 22 juni 2010 @ 11:19:
[...]
[ gedimensioneerde matrix ]
Hoewel dat een mogelijke oplossing is, is het natuurlijk nonsens dat jouw code correct is en die van de TS niet. Wellicht wil de TS wel gewoon een dynamische Matrix klasse, en de grootte dus niet compile-time vastleggen.
Nonsens? Als je ziet dat het return type van operator* afhangt van de matrix grootte, dan volgt daar direct uit dat de matrix grootte een compile-time gegeven moet zijn.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik had het over de CMatrix<Type> code van de TS in het algemeen, niet de foutieve definitie van operator*.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1