C ++ kengaytmali funktsiyalarmi?

C ++ uchun kengaytmalar mavjudmi?

Masalan, C# da siz quyidagilarni amalga oshirishingiz mumkin:

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

someuint.SwapEndian();

C ++ da shunga o'xshash narsa bormi?

13
Yo'q, C ++ bunga o'xshamaydi. AFAIK, bu haqda C ++ 17 uchun takliflar yo'q (yozing!). D dasturlash tilida UFCS (yagona funktsiya chaqiruvi sintaksisi) mavjud. Bu "kengaytma usullari" dir. Siz o'zingizning do'stingiz bo'lmagan funktsiyalarni yozishingiz va ob'ekt parametrlaridan qochishingiz mumkin, chunki natijada "in-out/right-to-out" o'rniga chapdan o'ngga o'qish mumkin bo'lgan kod paydo bo'ladi. chap ".
qo'shib qo'ydi muallif gnzlbg, manba
Bu C ++ dasturlari endi dasturiy til sifatida qaralishi mumkin bo'lmagan ko'plab sabablardan biridir. Bu juda eskirgan, C# kabi zamonaviy tillarning ko'pgina xususiyatlari yo'q va u ishlab chiqarishga qarama-qarshidir. Microsoft C #/Java kabi yangi kompilyatsiya qilingan til bilan qayta boshlash kerak. Agar kimdir menga C ++ dan foydalanishni taklif qilsa, men ularga kulib qo'yaman va ularni zudlik bilan o'zimdan ko'ra aqlli deb bilaman.
qo'shib qo'ydi muallif Krythic, manba

7 javoblar

C ++ da kengaytirish funktsiyalari mavjud emas. Siz ularni faqat erkin funksiyalar sifatida belgilashingiz mumkin.

uint SwapEndian(uint value){ ... }
5
qo'shib qo'ydi
Bu asl kodni ishlatadigan yagona javob; rahmat. Bundan tashqari, bo'sh funktsiyalar bilan nimani anglatishini va bu erda va boshqa joylarda aksariyat javoblarning bir misoli ko'rsatiladi. Bu bilan bog'liq bo'lgan boshqa mavzulardagi ba'zi javoblar OOPning uzoq falsafiy tavsifini va u qanday ishlashi kerakligini, ammo foydali tafsilotlarga ega emasligini ko'rsatadi.
qo'shib qo'ydi muallif user34660, manba

Kengaytma usullari (va "statik klasslar") C #/Java tillarida mavjud, chunki dizaynerlar OOPning (Java yo'li) Oddiy yo'l ekanligini va har bir narsa sinfdan bir usul bo'lishi kerak, deb qaror qildi:

Bu narsa C ++ ishi emas. C ++ da sizning ism-sharifingiz, bepul funktsiyalar va sinfning xatti-harakatlarini kengaytirish uchun Koenig qidirish mavjud:

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

Odatda kengaytma usullarini zararli deb hisoblayman. Agar sinfga nisbatan juda ko'p xatti-harakatlar qo'shsangiz, sinfning nima uchun borligining sababini qamrab ololmaysiz. Bundan tashqari, ("qisman sinflar" kabi) ular mahalliy bo'lmagan sinfga tegishli kodni yaratishga moyildirlar. Qaysi yomon.

Sizning muammolaringizga kelsak, C ++ da siz quyidagilarni amalga oshirasiz:

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;
}

Foydalanish:

swap_endian(42);

yoki agar bunday yozilsa:

std::uint64_t x = 42;
std::uint64_t y = swap_endian(x);
5
qo'shib qo'ydi
@gnzlbg, kelishib oldim. Ular C# da ba'zan juda qulay, ammo C# uslubi kengaytmasi usullari har doim bu ni T & (mos yozuvlar) sifatida oldi. Agar b (a) kodi ab() ga teng bo'lsa, b ning birinchi argumenti ko'rsatgich yoki mos yozuvlar bo'lishi kerak ? Nusxa ko'chirish mantiqiy emasmi? Ushbu sintaksisni C ++ da foydalanishni istayman va jamoa va qo'mita manfaatlaridan tashqari, uni kiritish uchun har qanday to'siq mavjudmi, deb so'raydi.
qo'shib qo'ydi muallif Drew Noakes, manba
Kengaytma usullari shunchaki sintaktik shakardir. Sizga act_on_bar bepul funktsiyasiga ega bo'lishingiz va uni act_on_bar (b) yoki b.act_on_bar() . Ikkinchisida faqat hech narsa yo'q bilan haqiqiy OO usuli , faqat okunabilirlik bilan amalga oshiriladi. Masalan, agar sizda 2 funktsiyasi bo'lsa; act2 (act1 (b)) b.act1() dan kamroq ukishdir. act2() . C ++ da siz majburiy act1 va act2 a'zo funktsiyalarini, ! Bundan tashqari, umumiy dasturiy ta'minotni yaxshilaydi. Bu erda o'qib chiqiladigan subyektiv sub'ektiv emas, chunki ko'pchilik odamlar chapdan o'ngga matnni o'qiydilar, tashqarida yo'q.
qo'shib qo'ydi muallif gnzlbg, manba
Lekin, albatta, bu tamoyil hech qanday sinfsiz qo'llanilishi mumkin. C sinf uchun funktsiyalarni sinflarga sinchkovlik bilan bog'laydigan sinflarni tobora kengaytirmasdan majbur qilmaslik. Funktsiyalar tuzilmalar yoki ibtidoiylarga hech qanday qarama-qarshilik yoki nazoratsiz to'qnashuvlarsiz bog'lashni istayman (men o'zimni bog'lab qo'yganim sababli, kompilyatsiya vaqtida tarjima qilinganligi sababli hech qanday mangling yo'q). Natijada ikkiliklarga nol abstraktsiya oqimi bilan erkin abstractions chiroyli.
qo'shib qo'ydi muallif Dmitry, manba

Bu kabi emas, balki siz mumkin emas siz yozmagan sinflarda ishlaydigan operatsion yuklarni yozadi va bu usul kengaytmalarga o'xshash (faqat nomlangan funksiyalar uchun emas, faqatgina operatorlar uchun emas). bu sinf tomonidan allaqachon belgilangan). Klassik misol, sinfingizni cout bilan ishlashga imkon beradi:

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

Bu, albatta, ahamiyatsiz misoldir, lekin siz bu fikrni olasiz.

3
qo'shib qo'ydi

To'g'ridan-to'g'ri o'xshash tarzda emas, balki ko'p marta shablon yordamida kerakli effektga erishish mumkin. Siz C ++ da aniq sinfga "original" usuldan "qo'shish" usullarini qo'sha olmaysiz, lekin siz har qanday turdagi ishlaydigan funktsional shablonlarni yaratishingiz mumkin.

Misol uchun, har qanday integral turdagi ntoh-turdagi konvertatsiya qilish uchun foydalanadigan funksiya shablon kutubxonasi:

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
qo'shib qo'ydi
@Alexandre: Bu siznikiga o'xshaydi. Faqatgina farq shundaki, ichki kodlar uchun ntohs va ntohl tomonidan ishlov beriladigan turlari uchun ixtisosliklarni taqdim qildim.
qo'shib qo'ydi muallif John Dibling, manba
Bu vazifani bajarish juda murakkab usul.
qo'shib qo'ydi muallif Alexandre C., manba
ntohx da Qaytish qatoridagi ikkita 32 bitli qiymatlar to'g'ri tartibda ekanligini qanday bilasiz? ;)
qo'shib qo'ydi muallif Géza Török, manba

Yo'q, afsus, lekin C ++ da shunga o'xshash narsa yo'q va u ham bo'lishi mumkin emas. Standartning amalga oshirishga bog'liqligi juda ko'p narsalar mavjud (ya'ni kompilyator uni xohlagan uslubda bajarishi mumkin) va C ++ da standartlashtirilmagan ABI .

1
qo'shib qo'ydi
@Sean: Kengaytma usullari ushbu stsenariyni qo'llab-quvvatlashi kerak bo'ladi: Siz uchinchi tomon kutubxonasi A va o'zingizning kodingiz (kutubxona B), A. belgilagan turlari bo'yicha kengaytma usullarini belgilaydi va foydalanadi. turli xil derleyici/bog'lanuvchi tomonidan juda yaxshi ishlab chiqilgan A ehtimollikdagi oldingi ikkilikiga qo'ng'iroqlarni qanday qilib ulash kerakligini aniqlang. Agar ABIda standartlashtirish mavjud bo'lmaganda (bu ochiqchasiga miloddan avvalgi tanaffuslar tufayli sodir bo'lmaydi) siz buni hech qachon qilolmaysiz.
qo'shib qo'ydi muallif Jon, manba
@Sean: Boshqa usulni qo'yish uchun: .NET da assotsiatsiyalarda mavjud bo'lgan turlari bo'yicha juda batafsil metadata bo'lishi kerak. C ++ OTOH da ikkilik ichida hech narsa yo'qligini, sizda aniqlagan ba'zi bir sinfni o'z ichiga olgan, uning nomini va boshqa tafsilotlarini eslamaslik uchun ishora ga ham bor.
qo'shib qo'ydi muallif Jon, manba
"Hech qachon bo'lishi mumkin emas", deb o'ylayman "biroz asossiz". Agar ular istamasalar, kengaytirish uslubi mexanizmining bir qismini qo'shishlari mumkin edi, ular tanlamaganlar.
qo'shib qo'ydi muallif Sean, manba

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.

Oxir-oqibat, ishlab chiqarishga harakat qilayotgan kodning uslubi, saqlanishi, ishonchliligi va tozaligi masalasiga aylanadi. "SubtractValue" usulini ikkita argumentlar yanada samarali kod yaratgan holda aniqlash mumkin, ammo boshqalar ortiqcha yuklangan usulni yanada himoyalanish mumkin deb hisoblashadi. Oxir-oqibat, mimarlar va ishlab chiquvchilar o'z loyihalari uchun muhim bo'lgan narsani aniqlab olishlari kerak. Men bu masalani hal etishga imkon beraman.

#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;
}

Yuqoridagi kod chiqishi "Qiymati 20" dir.

1
qo'shib qo'ydi

Agar this -qualified method parameteriga murojaat qilmoqchi bo'lsangiz, unda yo'q. Ammo, sizning ma'lum foydalanish holatlaringizga bog'liq boshqa ba'zi aqlli fokuslar bo'lishi mumkin ... Batafsil ma'lumot bera olasizmi?

1
qo'shib qo'ydi