Подтвердить что ты не робот

Функции расширения С++?

Существуют ли расширения для С++, например, в С#?

Например, в С# вы можете:

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

someuint.SwapEndian();

Есть ли что-нибудь подобное в С++?

4b9b3361

Ответ 1

Методы расширения (а также "статические классы" ) существуют на языках С#/Java только потому, что дизайнеры решили, что (способ Java) OOP - это One True Way и что все должно быть методом из класса:

Это не С++ способ делать вещи. В С++ у вас есть пространства имен, свободные функции и поиск 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

Я обычно считаю, что методы распространения вредны. Если вы придаете слишком много поведения классу, вы, вероятно, не сможете понять причину, по которой этот класс существует. Также (например, "частичные классы" ) они, как правило, делают код связанным с классом не локальным. Что плохо.

Что касается вашей проблемы, на С++ вы просто выполните:

template <typename T>
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<std::uint32_t>(42);

или, если тип можно вывести:

std::uint64_t x = 42;
std::uint64_t y = swap_endian(x);

Ответ 2

В С++ нет функций расширения. Вы можете просто определить их как свободные функции.

uint SwapEndian(uint value){ ... }

Ответ 3

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

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

Это, конечно, тривиальный пример, но вы получаете идею.

Ответ 4

Не по-аналогичному, но много раз вы можете добиться желаемого эффекта с помощью шаблонов. Вы не можете "добавлять" методы к конкретному классу на С++ без получения исходного класса, но вы можете создавать шаблоны функций, которые работают с любым типом.

Например, здесь библиотека шаблонов функций, которую я использую для преобразования ntoh-типа любого интегрального типа:

template<class Val> 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<Val*>(out));
}

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

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

template<> inline uint64_t ntohx<uint64_t>(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<float>(const float& v)
{
    uint32_t const* cast = reinterpret_cast<uint32_t const*>(&v);
    uint32_t ret = ntohx(*cast);
    return *(reinterpret_cast<float*>(&ret));
};

Ответ 5

Нет, извините, но в С++ ничего подобного нет, и этого никогда не может быть. Есть много вещей, которые стандарт оставляет как зависящие от реализации (т.е. Компилятор может делать это любым способом, который он предпочитает), а также С++ не имеет стандартного ABI.

Ответ 6

Если вы ссылаетесь на параметр this -qualified method, то нет. Но могут быть и другие умные трюки в зависимости от вашего конкретного случая использования... Можете ли вы предоставить более подробную информацию?

Ответ 7

Один из методов, который я нашел, - использовать перегруженный оператор " → " с лямбда-выражениями. Следующий код демонстрирует это. Вы должны знать, использовать оператор " → " вместо "- > " , потому что используемый мною компилятор не позволяет перегружать оператор "- > " . Кроме того, поскольку оператор " → " имеет более низкий приоритет, чем "- > " , вы должны использовать круглые скобки, чтобы заставить компилятор оценивать уравнение в правильном порядке.

В конце концов, это становится вопросом стиля, ремонтопригодности, надежности и чистоты кода, который вы пытаетесь создать. Можно было бы утверждать, что определение метода "SubtractValue" с двумя аргументами создает более эффективный код, но другие утверждают, что перегруженный метод более удобен в обслуживании. В конце концов, архитекторам и разработчикам необходимо определить, что важно для их проекта. Я просто предлагаю возможное решение проблемы.

#include <functional>
#include <iostream>
#include <stdio.h>
#include <tchar.h>

// 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<DemoClass *(DemoClass *obj)> 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".

Ответ 8

Как правило, нет, но вы можете добиться аналогичного поведения с помощью оператора оператора для объединения вашего типа и объекта std::function -convertible. Смотрите этот ответ.