Функцыі пашырэння C ++?

Існуюць пашырэння для C ++, як ёсць у C #?

Напрыклад, у C# вы можаце зрабіць:

public static uint SwapEndian(this uint value)
{
    var tmp = BitConverter.GetBytes(value);
    Array.Reverse(tmp);
    return BitConverter.ToUInt32(tmp, 0);
}

someuint.SwapEndian();

Ці ёсць што-небудзь падобнае ў C ++?

13
Не, C ++ не мае нічога падобнага. AFAIK, няма ніякіх прапаноў для C ++ 17 пра гэта альбо (напісаць адзін!). Мова праграмавання мае D UFCS (аднастайны сінтаксіс выкліку функцыі), што менавіта «метаду пашырэння». Было б нядрэнна мець функцыю, так як вы маглі б напісаць не-аднаму функцый, якія не з'яўляюцца членамі і пазбягаць параметру аб'екта, у выніку чаго код, які можа быць прачытаны злева-направа, а не «в-к-вонкі/направа-to налева ».
дададзена аўтар gnzlbg, крыніца
Гэта адна з многіх прычын, чаму C ++ нельга лічыць правільны мова праграмавання больш. Гэта занадта састарэлі, не хапае шматлікіх функцый сучасных моў, такіх як C#, і з'яўляецца супрацьлегласцю прадуктыўнымі. Microsoft неабходна пачаць нанова з новым скампіляванага мовай, які выглядае як C #/Java. Калі хто-то кажа мне, што яны выкарыстоўваюць C ++, я смяюся над імі і адразу ж убачыць іх як менш разумныя, чым я сам.
дададзена аўтар Krythic, крыніца

7 адказы

Там няма пашырэння функцый у C ++. Вы можаце проста вызначыць іх як свабодныя функцыі.

uint SwapEndian(uint value){ ... }
5
дададзена
Гэта адзіны адказ, які выкарыстоўвае зыходны код; Дзякуй. Гэта таксама паказвае прыклад таго, што маецца на ўвазе пад <я> свабодныя функцыі і амаль кожны іншы адказ тут і ў іншых месцах няма. Некаторыя адказы ў іншых патоках, звязаных з гэтым даць доўгае філасофскае апісанне аб'ектна-арыентаванае праграмаванне і як гэта павінна працаваць, але не мае карысныя дэталі.
дададзена аўтар user34660, крыніца

Метады пашырэння (а таксама «статычныя класы») існуюць ў C #/Java моў толькі таму, што дызайнеры вырашылі, што (Java-шлях) ААП Адзінага Праўдзівага Шляхі і што ўсё павінна быць метадам з класа:

Гэта не C ++ спосаб рабіць рэчы. У C ++ у вас ёсць прастора імёнаў, свабодныя функцыі і Koenig пошуку пашырыць паводзіны класа:

namespace foo
{
    struct bar { ... };

    void act_on_bar(const bar& b) { ... };
}

...

foo::bar b;
act_on_bar(b);//No need to qualify because of Koenig lookup

Я звычайна разглядаю метады пашырэння шкодныя. Калі вы надаяце занадта шмат паводзінаў у класе, вы proabably не ў стане захапіць прычыну, чаму клас існуе. Таксама (як «частковыя класы»), яны, як правіла, каб зрабіць код, які адносіцца да класа не мясцоваму. Што дрэнна.

Што тычыцца вашай праблемы, у C ++ вы проста зрабіць:

template 
T swap_endian(T x)
{
    union { T value; char bytes[sizeof(T)]; } u;
    u.value = x;

    for (size_t i = 0; i < sizeof(T)/2; i++) 
        swap(u.bytes[i], u.bytes[sizeof(T) - i - 1]);

    return u.value;
}

выкарыстанне:

swap_endian(42);

або, калі тып можа быць выведзена:

std::uint64_t x = 42;
std::uint64_t y = swap_endian(x);
5
дададзена
@gnzlbg, пагадзіўся. Яны былі вельмі зручныя ў C# час ад часу, аднак метады пашырэння # стылю C заўсёды бралі гэта , як неконстантный T & (спасылку). У выпадку Б (а) быць эквівалентна AB() , будзе першым аргументам б павінен быць паказальнікам ці спасылкай ? Ці будзе калі-небудзь мець сэнс скапіяваць? Я хацеў бы гэты сінтаксіс будзе даступны ў C ++ і цікава, ці ёсць якія-небудзь бар'еры на гэта ўвядзенне, акрамя абыякавасці з боку грамадства і камітэта.
дададзена аўтар Drew Noakes, крыніца
Метады пашырэння проста сінтаксічны цукар .. вы можаце мець бясплатную функцыю act_on_bar і выкарыстоўваць яго як act_on_bar (б) або b.act_on_bar() , Другі спосаб мае <я> нічога рабіць з <я> праўдзівы шлях ГА , толькі з дыскрэтнасцю. напрыклад калі ў вас ёсць 2 функцыі; <Код> act2 (ACT1 (б)) менш, чым чытаны b.act1 (). Act2() . У C ++ вы <я>, павінны быць зрабіць ACT1 і act2 Функцыі-сябры, каб атрымаць чытаны код, нават калі ён будзе мець больш сэнсу, каб мець іх як свабодныя функцыі ! Было б таксама палепшыць агульны праграмаванне занадта. І чытаны ня суб'ектыўны тут, так як большасць людзей чытаюць тэкст злева направа, ні в-к-аўт.
дададзена аўтар gnzlbg, крыніца
Але, вядома, гэты прынцып можа быць ужыты без класаў наогул. Для сырога C, каб звязаць функцыю з класамі, не прымушаючы іх пастаянна расце структуру, якая з'яўляецца класам. Я хачу, C, дзе я магу звязаць функцыі для структур або прымітываў без сувязі, ні бескантрольнага перакручваючы (так я звязваю сябе, няма коверкания, так як яно перакладаецца падчас кампіляцыі). Бясплатныя абстракцыі з нулявой уцечкай абстракцыі ў выніку двайковыя файлы прыгожа.
дададзена аўтар Dmitry, крыніца

Не падабаецца, але вы можна аператар запісы перагрузкі, якія працуюць на класах вы не пісалі, і гэта трохі падобна як метад пашырэнне (але не для названых функцый, толькі для аператараў, якія не былі вызначаецца тым, што клас уже). Класічным прыкладам робіць свой клас працы з соиЬ :

class MyClass {
public:
    MyClass(const char* blah) : str(blah) { }

    const char* string() const {
        return str;
    }

private:
    const char* str;
};

// this is kinda like a method extension
ostream& operator<<(ostream& lhs, const MyClass& rhs) {
    lhs << rhs.string();
}

// then you can use it like this
MyClass m("hey ho");
cout << m;

// prints hey ho

Гэта трывіяльны прыклад, вядома, але вы атрымаеце ідэю.

3
дададзена

Не ў наўпрост аналагічным чынам, але шмат разоў, вы можаце дасягнуць жаданага эфекту з дапамогай шаблонаў. Вы не можаце «дадаць» метады для канкрэтнага класа ў C ++ без вываду з зыходнага класа, але вы можаце стварыць шаблоны функцый, якія працуюць з любым тыпам.

Напрыклад, вось бібліятэка шаблонаў функцыі я выкарыстоўваю, каб зрабіць пераўтварэнне ntoh тыпу любога інтэгральнага тыпу:

template inline Val ntohx(const Val& in)
{
    char out[sizeof(in)] = {0};
    for( size_t i = 0; i < sizeof(Val); ++i )
        out[i] = ((char*)&in)[sizeof(Val)-i-1];
    return *(reinterpret_cast(out));
}

template<> inline unsigned char ntohx(const unsigned char & v )
{
    return v;
}
template<> inline uint16_t ntohx(const uint16_t & v)
{
    return ntohs(v);
}

template<> inline uint32_t ntohx(const uint32_t & v)
{
    return ntohl(v);
}

template<> inline uint64_t ntohx(const uint64_t & v)
{
    uint32_t ret [] =
    {
        ntohl(((const uint32_t*)&v)[1]),
        ntohl(((const uint32_t*)&v)[0])
    };
    return *((uint64_t*)&ret[0]);
}
template<> inline float ntohx(const float& v)
{
    uint32_t const* cast = reinterpret_cast(&v);
    uint32_t ret = ntohx(*cast);
    return *(reinterpret_cast(&ret));
};
2
дададзена
@Alexandre: Гэта тое ж самае, як ваша. Адзінае адрозненне складаецца ў тым, што я падаў спецыялізацый для ўбудаваных тыпаў, якія могуць быць апрацаваны з дапамогай ntohs і ntohl .
дададзена аўтар John Dibling, крыніца
Гэта даволі складаны шлях для выканання пастаўленай задачы.
дададзена аўтар Alexandre C., крыніца
у ntohx як вы ведаеце, два 32-бітных значэнняў у зваротным масіве ў правільным парадку? ;)
дададзена аўтар Géza Török, крыніца

Не, прабачце, але няма нічога падобнага, што ў C ++, і гэта таксама можа ніколі не быць. Ёсць шмат рэчаў, што стандартныя лісты, як залежаць ад рэалізацыі (гэта значыць кампілятар можа зрабіць гэта так, як ён аддае перавагу), а таксама C ++ не мае стандартызаваных ABI .

1
дададзена
@Sean: Метады пашырэння павінны падтрымліваць гэты сцэнар: вы бераце загалоўкі нейкай трэцяй бокам бібліятэкі A і свой уласны код (бібліятэка B), які вызначае і выкарыстоўвае метады пашырэння на тыпы, вызначаныя ў A. Ваш линкер затым трэба працаваць, які лучыць выклікі да магчымага двайковай папярэдне пабудаванай а , які можа вельмі добра быў выраблены з дапамогай іншага кампілятара/линкером . Калі няма стандартызацыі на ABI (які адкрыта не будзе адбывацца з-за нашай эры перапынкаў) вы не можаце калі-небудзь рабіць.
дададзена аўтар Jon, крыніца
@Sean: Кажучы па-іншаму: у .NET, вузлы павінны ўтрымліваць надзвычай падрабязныя метададзеныя пра тыпы, якія яны ўтрымліваюць. У C ++ OTOH няма абсалютна ніякай гарантыі, што ёсць нешта ўнутры двайковага файла, каб нават <я> намёк , што ён утрымлівае клас, пэўны, не кажучы яго імя і іншыя дэталі.
дададзена аўтар Jon, крыніца
Я думаю, што сказаць «яна таксама можа ніколі не быць» крыху неразумна. Яны маглі б дадаць некаторы механізм метады пашырэння, калі яны хочуць, яны проста вырашылі не.
дададзена аўтар Sean, крыніца

One method I have found is to use the overloaded ">>" operator with lambda expressions. The following code demonstrates this. You have to know to use operator ">>" instead of "->", this is because the compiler I use will not allow the operator "->" to be overloaded. Also because the operator ">>" has lower precedence than the "->" you have to use parentheses to force to compiler to evaluate the equation in the correct order.

У рэшце рэшт, гэта становіцца справай стылю, рамонтапрыдатнасць, надзейнасці і чысціні кода вы спрабуеце вырабіць. Можна было б сцвярджаць, вызначэнне метаду «SubtractValue» з двума аргументамі стварае больш эфектыўны код, але іншыя сцвярджаюць, перагружаны метад становіцца больш суправаджаецца. У рэшце рэшт ён пакінуў архітэктараў і распрацоўшчыкаў, каб вызначыць, што важна для іх праекта. Я проста прадастаўленне магчымага рашэння гэтай праблемы.

#include 
#include 
#include 
#include 

// Some plain demo class that cannot be changed.
class DemoClass
{
public:
    int GetValue() { return _value; }
    int SetValue(int ivalue) { _value = ivalue; return _value; }
    DemoClass *AddValue(int iadd) { this->_value += iadd; return this; }

private:
    int _value = 0;
};

// Define Lambda expression type that takes and returns a reference to the object.
typedef std::function DemoClassExtension;

// Overload the ">>" operator because we cannot overload "->" to execute the extension.
DemoClass* operator>>(DemoClass *pobj, DemoClassExtension &method)
{
    return method(pobj);
}

// Typical extensions.

// Subtract value "isub".
DemoClassExtension SubtractValue(int isub)
{
    return [=](DemoClass *pobj) {
        pobj->AddValue(-isub);
        return pobj;
    };
}

// Multiply value "imult".
DemoClassExtension MultiplyValue(int imult)
{
    return [=](DemoClass *pobj) {
        pobj->SetValue(pobj->GetValue() * imult);
        return pobj;
    };
}

int _tmain(int argc, _TCHAR* argv[])
{
    DemoClass *pDemoObject = new DemoClass();
    int value = (pDemoObject->AddValue(14) >> SubtractValue(4) >> MultiplyValue(2))->GetValue();
    std::cout << "Value is " << value;
    return 0;
}

Вышэй выхадны код «Значэнне 20».

1
дададзена

Калі вы маеце на ўвазе гэта -qualified параметр метаду, то няма. Але могуць быць некаторыя іншыя хітрыя трукі ў залежнасці ад канкрэтнага выпадку выкарыстання ... Ці можаце вы даць больш падрабязную інфармацыю?

1
дададзена