3

想知道有用的用途operator ,我正在尝试创建一组帮助器对象,以便更轻松地从 C++ 代码构建 DB 查询。我的想法是利用operator ,类似于数据库调用的创建指令的顺序。辅助对象如下:

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

然后,使用正确typedef的 s 和值,此方法允许执行以下操作:

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

AFAIK 这个专业化必须有三个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,。这就像 ifORDER_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 ,专业化,但是在同一个命名空间上添加一个自由运算符可以修复编译错误:

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

Herb Sutter在这篇相关文章中解释了这个问题。该文章与您的问题并不完全相关,但包含解决方案。特别是,请查看“示例 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"求值,并返回对 的引用A。然后,根据该表达式的结果,A::operator , (3.2)将调用该函数。A有一个可行的功能,即接受int. 而那个被选中。B看不到 的重载,因为第一个表达式的结果b, "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 回答