3

C ++コードからのDBクエリの構築を容易にするoperator ,ために、ヘルパーオブジェクトの小さなセットを作成しようとしています。私の考えはoperator ,、DB呼び出しに似た命令を作成するためにを利用することです。ヘルパーオブジェクトは次のとおりです。

class Fields
{
public:
    Fields &operator ,(const std::string &s)
    {
        SQL.append(s).append(1, ',');
        return *this;
    }

    Fields &operator ,(const Fields &f)
    {
        std::string Result = f;
        SQL.append(Result);
        return *this;
    }

    virtual operator std::string() const = 0;
protected:
    std::string SQL;
};

template <const char *INSTRUCTION> struct Instruction : public Fields
{
    operator std::string() const
    {
        std::string Result(INSTRUCTION);
        return Result.append(SQL);
    }
};

次に、正しいtypedefsと値を使用して、このアプローチにより次のことが可能になります。

extern const char SQL_SELECT[] = "SELECT ";
extern const char SQL_FROM[] = "FROM ";
extern const char SQL_WHERE[] = "WHERE ";
extern const char SQL_ORDER_BY[] = "ORDER BY ";

typedef Instruction<SQL_SELECT> SELECT;
typedef Instruction<SQL_FROM> FROM;
typedef Instruction<SQL_WHERE> WHERE;
typedef Instruction<SQL_ORDER_BY> ORDER_BY;

std::string Query = ((SELECT(), "a", "b", "c"),
                     (FROM(), "A", "B"),
                     (WHERE(), "a = b AND c <> b"),
                     (ORDER_BY(), "a", "c"));

std::cout << Query;

これはこの出力を生成します:(SELECT a,b,c,FROM A,B,WHERE a = b AND c <> b,ORDER_BY a,c,私のバージョンでは末尾のコンマを処理しています。例を短くするためにこの部分は省略されています)、ここにコードがあります

問題は命令ORDER BYです。operator ,この命令は、順序付けの動作を変更する最後のオペランドを取ることができます。列挙値をstruct Instructionインスタンスに(通過させて)渡したいと思います。

enum ORDER
{
    ASC,
    DESC,
};

std::string OrderBy = (ORDER_BY(), "a", "c", DESC); // <---- Note the 'DESC' value.

ただし、インスタンスに対してこの演算子を有効にしたいだけInstruction<SQL_ORDER_BY>なので、テンプレートを特殊化しようとしました。

template <> struct Instruction<SQL_ORDER_BY> : public Fields
{
    Instruction() : order(ASC) {}

    Fields &operator ,(const ORDER o)
    {
        order = o;
        return *this;
    }

    operator std::string() const
    {
        std::string Result(SQL_ORDER_BY);
        Result.append(SQL);
        Result.append(order == ASC? "ASC": "DESC");
        return Result;
    }

private:
    ORDER order;
};

このスペシャライゼーションには、次の3つのoperator ,オーバーロードが必要です。

  • Fields &operator ,(const Fields &)
  • Fields &operator ,(const std::string &)
  • Fields &operator ,(const ORDER)

ただし、特殊化を作成した後、クエリ文字列は次のようになります。

std::string Query = ((SELECT(), "a", "b", "c"),
                     (FROM(), "A", "B"),
                     (WHERE(), "a = b AND c <> b"),
                     (ORDER_BY(), "a", "c"));

値を持つことを終了します:SELECT a,b,c,FROM A,B,WHERE a = b AND c <> b,c,。これは、無視された場合と同様であり、値ORDER_BYを追加するとコンパイルエラーが発生します。DESC

std::string Query = ((SELECT(), "a", "b", "c"),
                     (FROM(), "A", "B"),
                     (WHERE(), "a = b AND c <> b"),
                     (ORDER_BY(), "a", "c", DESC)); // <-- cannot convert 'ORDER' to 'string'

ORDERスペシャライゼーションに値が入力されていないようですがoperator ,、同じ名前空間にfree演算子を追加すると、コンパイルエラーが修正されます。

std::string operator ,(const std::string &left, const ORDER right)
{
    std::string Result(left);
    return Result.append(1, ',').append(right == ASC? "ASC": "DESC");
}

しかし、私は本当にそれFields &Instruction<SQL_ORDER_BY>::operator ,(const ORDER)が呼ばれるだろうと思ったので、私は今いくつかのアドバイスを求めています:

  1. Instruction<SQL_ORDER_BY>テンプレートを特殊化した後、インスタンスがクエリ文字列に追加されないのはなぜですか?
  2. 値がスペシャライゼーションによって提供されたものORDERを呼び出さない理由。Fields &operator ,(const ORDER)
  3. インスタンスoperator ,はいくつありますか?Instruction<SQL_ORDER_BY>

PS:この努力はすべて独学の目的であり、このコードのほぼゼロ行が本番コードになってしまうので、ライブラリの使用やコードの有用性についてのコメントは避けてください。

ありがとう。

編集:

彼の答えを削除した誰かが、専門分野に行を追加するようにアドバイスしました。それを行うと、無視の問題が修正されusing Fields::operator std::string;ましusing Fields::operator,;ORDER_BY

4

1 に答える 1

1

問題は、のサブクラスの,演算子のオーバーロードが、オーバーロードされた演算子をスーパークラスから隠しているという事実によるものです。これは、C ++で関数呼び出しの解決が機能する方法です。名前の検索が最初に行われ、特定の名前空間で名前のセットが見つかるとすぐに停止します。次に、過負荷解決が実行されます。Instruction<SQL_ORDER_BY>Fields

この問題は、ハーブサッターによるこの関連記事で説明されています。この記事はあなたの問題に完全に関連しているわけではありませんが、その解決策が含まれています。特に、「例2a」を確認してください。

usingディレクティブを使用して、Field基本クラスの演算子のオーバーロードを派生クラスのスコープにインポートする必要があるため、,inのオーバーロードによってInstruction<SQL_ORDER_BY>それらが非表示になることはありません。

この小さなプログラムを簡単な例として取り上げます。

#include <iostream>
#include <string>

using namespace std;

struct A // Class A contains two overloads of operator ,
{
    void operator , (int) { cout << "A::operator , (int)" << endl; }
    void operator , (string) { cout << "A::operator , (string)" << endl; }
};

struct B : A // Class B contains only *one* overload of operator ,
             // Overloads coming from `A` are *hidden* by this one
{
    void operator , (double) { cout << "B::operator , (double)" << endl; }
};

int main()
{
    A a;
    a, 1; // "A::operator , (int)" will be printed to std out
    a, "hello"; // "A::operator , (string)" will be printed to std out

    B b;
    b, 3.0; // "B::operator , (double)" will be printed to the std out
    b, "hello"; // Nothing in the standard output!
}

ただし、この方法で定義を変更した場合B

struct B : A
{
    using A::operator ,; // <-- Brings A's overloads into scope!
    void operator , (double) { cout << "B::operator , (double)" << endl; }
};

main()上記のサンプルプログラムの最後の行で、これが標準出力に出力されることがわかります。

A::operator , (string)

これはB、演算子の'オーバーロードが、で,定義されたオーバーロードを隠さないことを意味しAます。これは、おそらくあなたが望むものです。

アップデート:

答えがまだカバーされていない別の問題があります。,基本クラスの演算子のオーバーロードは、Fieldsタイプのオブジェクトへの参照を返しますFields,演算子は左側に関連付けられているため、式は。e1, e2, e3として評価され(e1, e2), e3ます。特定のケースでは、の結果は、派生クラスでサポートされている演算子(e1, e2)のオーバーロードをサポートしていない基本クラスへの参照です。,

もう一度、設計を反映したより単純な例に減らしてみましょう。

#include <iostream>
#include <string>

using namespace std;

struct A
{   
    // Operator overloads return a reference to A
    A& operator , (int) 
    { cout << "A::operator , (int)" << endl; return *this; }

    A& operator , (string) 
    { cout << "A::operator , (string)" << endl; *this; }
};

struct B : A
{   
    // Imported overloads still return a reference to A
    using A::operator ,;

    // This overload returns a reference to B 
    B& operator , (double) 
    { cout << "B::operator , (double)" << endl; return *this; }
};

int main()
{
    B b;
    b, 3.0;
    b, "hello", 3.2; // What will be displayed here?
}

例の最後の行を考えてみましょう。おそらくそれが呼び出すことを期待しますB::operator , (double)が、これは標準出力に出力されるものです。

A::operator , (int)

なんで?ええと、コンマ演算子の結合性とオーバーロードの戻り型のためです。最初に、式が評価され、それがへの参照をb, "hello"返します。次に、この式の結果に基づいて、関数が呼び出されます。を受け入れる実行可能な機能があります。そして、その1つが選択されます。最初の式の結果がタイプであるため、のオーバーロードは表示されません。AA::operator , (3.2)AintBb, "hello"A&

それで、それをどのように解決するのですか?CRTP( "Curiously Recurring Template Pattern")と呼ばれるデザインパターンを使用して、との定義を次のように変えることができAますB

template<typename T>
struct A
{
    T& operator , (int) 
    { cout << "A::operator , (int)" << endl; return *(static_cast<T*>(this)); }

    T& operator , (string) 
    { cout << "A::operator , (string)" << endl; *(static_cast<T*>(this)); }
};

struct B : A<B>
{
    using A::operator ,;
    B& operator , (double) 
    { cout << "B::operator , (double)" << endl; return *this; }
};

このように、上記の例の関数の最後の行はmain()、標準出力に期待するものを出力します。

A::operator , (string)
B::operator , (double)
于 2013-01-28T15:28:21.073 に答える