[C++] template, implicit conversion

Pagina: 1
Acties:

  • JeromeB
  • Registratie: September 2003
  • Laatst online: 15-11 14:27
Ik probeer een aantal operators te overloaden zodat ze werken met de std::vector. Nu wilde ik dit generaliseren door templates te gebruiken.

Ik heb een klein voorbeeldje uitgewerkt:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<typename T>
std::vector<T> operator+=(std::vector<T>& a, T b)
{
    for(typename std::vector<T>::iterator i = a.begin(); i!=a.end(); ++i)
    {
        (*i) += b;
    }
    return a;
}
std::vector<std::string> vec1;
vec1.push_back("hoi");
vec1.push_back("test");
vec1.push_back("bla");
std::vector<std::string> vec2(vec1);

vec1 += std::string("extra"); // dit werkt prima
vec2 += "extra"; // dit werkt niet.


Ik krijg een foutmelding: no match for 'operator+=' in 'vec2+= "extra"'

Als ik een extra typename gebruik dan werkt het wel:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<typename T1, typename T2>
std::vector<T> operator+=(std::vector<T1>& a, T2 b)
{
    for(typename std::vector<T1>::iterator i = a.begin(); i!=a.end(); ++i)
    {
        (*i) += b;
    }
    return a;
}
std::vector<std::string> vec1;
vec1.push_back("hoi");
vec1.push_back("test");
vec1.push_back("bla");
std::vector<std::string> vec2(vec1);

vec1 += std::string("extra"); // dit werkt prima
vec2 += "extra"; // dit werkt nu wel prima


Waarom krijg ik in het eerste geval een foutmelding, maar in het tweede geval niet? Heeft dit te misschien te maken met conversion?

PC load letter? What the fuck does that mean?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:31

.oisyn

Moderator Devschuur®

Demotivational Speaker

Hij kan T voor operator+=<vector<T>, T> niet deduceren. Want je roept 'm aan met (vector<std::string>, const char[]), dus wat is T dan, een std::string of een const char[]? Hierdoor wordt deze variant van operator+= niet meegenomen in de lijst van mogelijke overloads, en dus krijg je een error dat hij geen goede operator+= kan vinden.

.edit: Waarom return je trouwens een kopie van de vector? Dat is niet gebruikelijk bij assignment operators (en in dit geval zelfs erg inefficient). En voldoet std::valarray niet al gewoon aan je eisen?

[ Voor 25% gewijzigd door .oisyn op 30-03-2007 17:46 ]

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.


  • JeromeB
  • Registratie: September 2003
  • Laatst online: 15-11 14:27
.oisyn schreef op vrijdag 30 maart 2007 @ 17:39:
Hij kan T voor operator+=<vector<T>, T> niet deduceren. Want je roept 'm aan met (vector<std::string>, const char[]), dus wat is T dan, een std::string of een const char[]? Hierdoor wordt deze variant van operator+= niet meegenomen in de lijst van mogelijke overloads, en dus krijg je een error dat hij geen goede operator+= kan vinden.
Toch wel jammer, ik had eigenlijk gehoopt dat het tweede argument afhankelijk zou zijn van het eerste argument, maar dat blijkt dus niet zo te zijn.
.oisyn schreef op vrijdag 30 maart 2007 @ 17:39:
.edit: Waarom return je trouwens een kopie van de vector? Dat is niet gebruikelijk bij assignment operators (en in dit geval zelfs erg inefficient). En voldoet std::valarray niet al gewoon aan je eisen?
Wat zou jij 'returnen' ? void?

Ik had eigenlijk nog helemaal niet gekeken naar std::valarray. Ik zal het eens even opzoeken.

PC load letter? What the fuck does that mean?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:31

.oisyn

Moderator Devschuur®

Demotivational Speaker

JeromeB schreef op vrijdag 30 maart 2007 @ 17:59:

Toch wel jammer, ik had eigenlijk gehoopt dat het tweede argument afhankelijk zou zijn van het eerste argument, maar dat blijkt dus niet zo te zijn.
Waarom dan? 't Is niet alsof die belangrijker is oid :), en dus is een deducering als (std::vector<std::string>, std::string) niet beter of slechter dan (std::vector<const char[]>, const char[]).
Wat zou jij 'returnen' ? void?
Een reference naar de vector, zoals gebruikelijk bij assignment operators :)

[ Voor 46% gewijzigd door .oisyn op 30-03-2007 18:11 ]

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.


  • JeromeB
  • Registratie: September 2003
  • Laatst online: 15-11 14:27
.oisyn schreef op vrijdag 30 maart 2007 @ 18:09:
[...]

Waarom dan? 't Is niet alsof die belangrijker is oid :), en dus is een deducering als (std::vector<std::string>, std::string) niet beter of slechter dan (std::vector<const char[]>, const char[]).
Daar heb je eigenlijk wel gelijk in.
.oisyn schreef op vrijdag 30 maart 2007 @ 18:09:
[...]

Een reference naar de vector, zoals gebruikelijk bij assignment operators :)
ok, zoiets dus? of kan ik die const beter weglaten?
C++:
1
2
3
4
5
6
7
8
9
template<typename T1, typename T2>
const std::vector<T1>& operator+=(std::vector<T1>& a, T2 b)
{
    for(typename std::vector<T1>::iterator i = a.begin(); i!=a.end(); ++i)
    {
        (*i) += b;
    }
    return a;
}



,bedankt voor de uitleg :)

PC load letter? What the fuck does that mean?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:31

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je zou evt. nog wel een kleine aanpassing kunnen maken aan die tweede variant zodat de functie alleen "bestaat" voor T2's die converteerbaar zijn naar T's, mbv SFINAE:
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
template<class FROM, class TO> struct IsConvertible
{
private:
    struct small { char c; };
    struct large { char c[2]; };
    static small foo(TO);
    static large foo(...);

public:
    static const bool value = (sizeof(foo(*((FROM*)0))) == sizeof(small));
};

template<class T, bool b> struct EnableIf { };
template<class T> struct EnableIf<T, true> { typedef T type; };


template<class T, class T2>
typename EnableIf<std::vector<T>&, IsConvertible<T2,T>::value>::type
operator += (std::vector<T>& a, T2 b)
{
    // ...
    return a;
}

struct Aap {};
struct Noot{};

int main()
{
    std::vector<float> v;
    v += 4;  // werkt prima, int is converteerbaar naar float
    std::vector<Aap> v2;
    v2 += Noot(); // error, geen overload voor operator+=(vector<Aap>, Noot)
}

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.


  • JeromeB
  • Registratie: September 2003
  • Laatst online: 15-11 14:27
_/-\o_ Nifty.

Ik begrijp nog niet helemaal hoe IsConvertible werkt, maar dat zoek ik nog wel even uit.

PC load letter? What the fuck does that mean?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:31

.oisyn

Moderator Devschuur®

Demotivational Speaker

IsConvertible heeft een overloaded functie foo(). De ene accepteert een TO (het type waar je naartoe wilt converteren), de ander accepteert alles. Door foo() aan te roepen met een FROM (die wordt aangemaakt door een nullpointer te dereferencen, "FROM()" werkt namelijk niet als FROM geen default constructor heeft), zal de compiler foo(TO) voorrang geven als FROM converteerbaar is naar TO, en anders wordt de versie die alles accepteert gebruikt. Aan de hand de grootte van het return-type kun je zien welke van de twee de compiler gebruikt heeft, en de sizeof zorgt er tevens voor dat er niet echt code wordt gegenereerd.

[ Voor 10% gewijzigd door .oisyn op 30-03-2007 18:41 ]

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.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:31

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik bedacht me trouwens dat hier een veel mooiere en simpelere oplossing voor was, namelijk om gewoon het tweede argument niet deducable te maken.

C++:
1
2
3
4
5
6
7
8
9
template<typename T> 
std::vector<T1>& operator+=(std::vector<T>& a, typename std::vector<T>::value_type b) 
{ 
    for(typename std::vector<T1>::iterator i = a.begin(); i!=a.end(); ++i) 
    { 
        (*i) += b; 
    } 
    return a; 
}


:)

Nou werkt jouw 2e usecase gewoon, omdat ie T altijd deduced naar std::string daar je een std::vector<std::string> opgeeft. Naar de const char[] wordt niet eens gekeken want het tweede argument is niet deduceerbaar. De signature na deducering wordt dus (std::vector<std::string>&, std::string>), en als je die een const char[] als tweede argument voert wordt ie dus automatisch geconverteerd naar een std::string.

[ Voor 33% gewijzigd door .oisyn op 03-04-2007 17:10 ]

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.


  • JeromeB
  • Registratie: September 2003
  • Laatst online: 15-11 14:27
Dat is inderdaad handig, bedankt.

ps. Je hebt nog twee kleine tikfoutjes gemaakt. T1 moet T zijn. ;)


Ik had trouwens ook even naar std::valarray gekeken, maar dat is niet wat ik zoek. std::valarray wijkt kwa interface iets teveel af van std::vector en er zijn volgensmij geen standaard-iterators voor de std::valarray.

[ Voor 51% gewijzigd door JeromeB op 03-04-2007 18:19 ]

PC load letter? What the fuck does that mean?

Pagina: 1