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(統一関数呼び出し構文)があります。これはまさに「拡張メソッド」です。メンバー以外の非友人の関数を書くことができ、オブジェクトパラメータを避けることができ、結果的に "イン・トゥ・アウト/ライト・ツー・ライト"の代わりに左から右に読むことができるコードが得られるので、左"。
追加された 著者 gnzlbg,
これは、C ++がもはや有効なプログラミング言語とはみなされない多くの理由の1つです。それは時代遅れであり、C#のような現代的な言語の多くの機能を欠いており、生産性の反対です。マイクロソフトはC#/ Javaのような新しいコンパイル言語を最初からやり直す必要があります。もし誰かが私にC ++を使っていると言えば、私はそれらを笑ってすぐに自分よりも賢明でないと見ます。
追加された 著者 Krythic,

7 答え

C ++には拡張機能はありません。それらを自由関数として定義することができます。

uint SwapEndian(uint value){ ... }
5
追加された
これは、元のコードを使用する唯一の応答です。ありがとうございました。また、自由な関数の意味の例と、他のすべての他の応答や他のすべての応答の例も示しています。これに関連する他のスレッドでのいくつかの応答は、OOPの長い哲学的記述と、それがどのように動作すると思われるかを示していますが、有用な詳細が不足しています。
追加された 著者 user34660,

デザイナーが(Javaのやり方で)OOPがThe One True Wayであり、すべてがクラスのメソッドでなければならないと決定したため、拡張メソッド(および「静的クラス」も)はC#/ 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

私は通常、拡張メソッドが有害であると考えます。クラスにあまりにも多くの振る舞いを付けると、クラスが存在する理由を捕らえることができなくなります。また(「部分クラス」のように)、クラスに関連するコードをローカル以外のものにする傾向があります。どちらが悪いですか。

あなたの問題については、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#のスタイル拡張メソッドは常に this を非const T&(参照)として使用していました。 b(a)ab()と同等の場合、 b の最初の引数はポインタまたは参照でなければなりません?それはコピーするのが理にかなっていますか?私はこの構文をC ++で利用できるようにしたいと思いますし、コミュニティや委員会からの無関心以外にも、その導入に関する障壁があるかどうか疑問に思っています。
追加された 著者 Drew Noakes,
act_on_bar(b)b.act_on_bar()のように自由な関数 act_on_bar を使用することができます。 。 2番目の方法では、真のOO方法で行うことは一切ありません。読みやすさだけです。例えば。 2つの機能がある場合。 act2(act1(b))b.act1()。act2()より読みにくいです。 C ++では、自由な関数として持たせるのが理にかなっていても、 act1 と act2 メンバ関数を読めるようにする必要があります。 !また、ジェネリックプログラミングも向上します。ほとんどの人が左から右にテキストを読んでいるので、読解は主観的ではありません。
追加された 著者 gnzlbg,
しかし、確かに原則はクラスなしで適用することができます。生のCが、関数をクラスに束縛することなく、クラスを常に成長する構造に強制します。私はC言語を必要とします。ここでは、カップリングや無制限のマングリングなしに構造体やプリミティブに関数をバインドすることができます(コンパイル時に変換されるので、自分自身をバインドするので、マングリングはありません)。結果のバイナリへの抽象リークのない無料の抽象化は美しいです。
追加された 著者 Dmitry,

そうではありませんが、書かなかったクラスで動作する演算子オーバーロードを書くことはできますが、メソッド拡張のようなものです(名前付き関数ではなく、すでにそのクラスによって定義されています)。古典的な例は、あなたのクラスを 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

これはもちろんの簡単な例ですが、あなたはその考えを得る。

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
追加された
@アレクサンドル:あなたと同じです。唯一の違いは、 ntohsntohl で処理できる組み込み型の特殊化を提供したことです。
追加された 著者 John Dibling,
これは、手元にあるタスクを実行するための非常に複雑な方法です。
追加された 著者 Alexandre C.,
in ntohx 戻り配列の2つの32ビット値が適切な順序であることをどのように知っていますか? ;)
追加された 著者 Géza Török,

いいえ、申し訳ありませんが、C ++のようなものはありません。標準が実装依存(つまり、コンパイラは好きなやり方でそれをやり遂げることができる)として残しているものがたくさんありますし、またC ++には標準化された ABI を参照してください。

1
追加された
@Sean:拡張メソッドでは、このシナリオをサポートする必要があります.Aで定義された型の拡張メソッドを定義して使用するサードパーティのライブラリAと独自のコード(ライブラリB)のヘッダを取得します。おそらく、別のコンパイラ/リンカーによって生成されたA の可能性のある、あらかじめビルドされたバイナリへの呼び出しを配線する方法を考えてください。 ABIに標準化がない限り(これは、BCブレークによって率直には起こりません)、あなたはこれを行うことはできません。
追加された 著者 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.

最終的には、生成しようとしているコードのスタイル、保守性、信頼性、およびクリーンさの問題になります。 2つの引数を使用して "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;
}

上記のコード出力は "Value is 20"です。

1
追加された

this -qualifiedメソッドのパラメータを参照している場合は、noです。しかし、あなたの特定のユースケースに応じて、他にもいくつかの巧妙なトリックがあるかもしれません...詳細を提供できますか?

1
追加された