简体   繁体   English

在专用模板中添加运算符重载

[英]Adding an operator overloading in specialized template

Wondering about useful uses of the operator , I'm trying to create a little set of helper objects to make easier the construction of DB queries from the C++ code. 想知道operator ,有用用途operator ,我正在尝试创建一些辅助对象,以便更容易地从C ++代码构造数据库查询。 My idea is to take advantage of the operator , in order of create instructions that resembles the DB calls. 我的想法是利用operator ,按照创建类似于DB调用的指令。 The helper objects are the following: 辅助对象如下:

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

Then, with the correct typedef s and values, this approach allows to do the following: 然后,使用正确的typedef和值,此方法允许执行以下操作:

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;

Which produces this output: SELECT a,b,c,FROM A,B,WHERE a = b AND c <> b,ORDER_BY a,c, (I'm taking care of the trailing commas in my version, this part is omitted in order to shorten the example), here is the code . 产生此输出: SELECT a,b,c,FROM A,B,WHERE a = b AND c <> b,ORDER_BY a,c, (我正在处理我的版本中的尾随逗号,此部分被省略为了缩短示例, 这里是代码

The problem is the instruction ORDER BY . 问题是指令ORDER BY This instruction can take a final operand that changes the behaviour of the ordering, I want to pass (through operator , ) an enum value to the struct Instruction instance: 该指令可以采用改变排序行为的最终操作数,我想将(通过operator , )枚举值传递给struct Instruction实例:

enum ORDER
{
    ASC,
    DESC,
};

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

But only want to enable this operator for the Instruction<SQL_ORDER_BY> instances, so I've tried to specialize the template: 但是只想为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 this specialization must have three operator , overloads: AFAIK这个专业化必须有三个operator ,重载:

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

But after creating the specialization, the Query string: 但在创建专门化后,查询字符串:

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

Ends having the value: SELECT a,b,c,FROM A,B,WHERE a = b AND c <> b,c, . 结束具有以下值: SELECT a,b,c,FROM A,B,WHERE a = b AND c <> b,c, . This is like if ORDER_BY is ignored, and adding the DESC value results in a compilation error: 这就像忽略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'

It seems that ORDER values aren't entering in the operator , of the specialization, but adding a free operator on the same namespace fixes the compilation error: 似乎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");
}

But I really thought that Fields &Instruction<SQL_ORDER_BY>::operator ,(const ORDER) would be called, so I'm now asking for some advice: 但我真的认为会调用Fields &Instruction<SQL_ORDER_BY>::operator ,(const ORDER) ,所以我现在要求一些建议:

  1. Why the Instruction<SQL_ORDER_BY> instances aren't appended to the Query string after specialize the template? 为什么在专门化模板后, Instruction<SQL_ORDER_BY>实例不会附加到查询字符串?
  2. Why the ORDER values aren't calling the Fields &operator ,(const ORDER) provided by the specialization. 为什么ORDER值不调用由专业化提供的Fields &operator ,(const ORDER)
  3. How many operator , have the Instruction<SQL_ORDER_BY> instances? 有多少operator ,Instruction<SQL_ORDER_BY>实例?

PS: All this effort is for autodidact purposes, almost zero lines of this code will end up in production code, so please avoid comments about using libraries or about the utility of the code, PS:所有这些努力都是为了自学成分目的,这段代码的几乎零行将最终出现在生产代码中,因此请避免关于使用库或代码实用程序的注释,

thanks. 谢谢。

EDIT: 编辑:

Someone who deleted his answer adviced to add using Fields::operator std::string; 有人删除了他的回答,建议using Fields::operator std::string; and using Fields::operator,; using Fields::operator,; lines to the specialization, doing it fixed the ignoring ORDER_BY problem. 行专业化,修复了忽略ORDER_BY问题。

The problem is due to the fact that your overload of the , operator in the Instruction<SQL_ORDER_BY> subclass of Fields is hiding the overloaded operators from the superclass. 这个问题是由于这样的事实:你的过载,操作者在Instruction<SQL_ORDER_BY>的子类Fields是从超类隐藏重载操作符。 This is just the way function call resolution works in C++: name lookup happens first and stops as soon as a set of names in a certain namespace is found; 这就是函数调用解析在C ++中的工作方式:名称查找首先发生,一旦找到某个名称空间中的一组名称就停止; then, overload resolution is performed. 然后,执行重载决策。

The problem is explained in this related article by Herb Sutter . Herb Sutter这篇相关文章中解释了这个问题。 The article is not entirely relevant to your problem, but contains the solution to it. 这篇文章与您的问题并不完全相关,但包含了解决方案。 In particular, check out " Example 2a ". 特别是,请查看“ 例2a ”。

You have to use using directives to import the Field base class's operator overloads into the scope of the derived classes, so your overload of , in Instruction<SQL_ORDER_BY> won't hide them. 你必须使用using指令的输入Field基类的运算符重载到派生类的范围,让你的过载,Instruction<SQL_ORDER_BY>不会隐藏它们。

Take this little program as a simple example: 把这个小程序作为一个简单的例子:

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

However, if you change the definition of B this way: 但是,如果以这种方式更改B的定义:

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

You will see that the last line of main() in the sample program above will print this to the standard output: 您将看到上面示例程序中main()的最后一行将打印到标准输出:

A::operator , (string)

This means that the B 's overload of the , operator no more hides the overloads defined in A , which is most likely what you want. 这意味着, B的过载,运营商没有更多的隐藏在定义重载A ,这是最有可能你想要什么。

UPDATE: 更新:

There is another problem that the answer did not cover yet. 另一个问题是答案尚未涵盖。 The overload of the , operator in your base class Fields returns a reference to an object of type Fields . 所述的过载,操作者在你的基类Fields返回类型的一个对象的引用Fields Since the , operator associates to the left, the expression e1, e2, e3 is evaluated as (e1, e2), e3 . 由于,操作者相关联的左侧,表达e1, e2, e3被评价为(e1, e2), e3 In your specific case, the result of (e1, e2) is a reference to a base class that does not support the overload of the , operator which is supported by the derived class. 在特定情况下,结果(e1, e2)是基类不支持的过载的参考,这是由派生类支持运营商。

Let's reduce it once again to a simpler example that mirrors your design: 让我们再次将它简化为一个反映您设计的简单示例:

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

Consider the last line of the example. 考虑示例的最后一行。 You probably expect it to invoke B::operator , (double) , but this is what is printed to the standard output: 您可能希望它调用B::operator , (double) ,但这是打印到标准输出的内容:

A::operator , (int)

Why? 为什么? Well, because of the associativity of the comma operator and the return type of your overloads. 好吧,因为逗号运算符和重载的返回类型的关联性。 First, the expression b, "hello" is evaluated, and that returns a reference to A . 首先,计算表达式b, "hello" ,并返回A引用 Then, on the result of this expression, the function A::operator , (3.2) will be invoked. 然后,在该表达式的结果上,将调用函数A::operator , (3.2) A has a viable function, which is the one accepting an int . A具有可行的功能,即接受int And that one gets selected. 而那一个被选中。 B 's overload is not seen because the result of the first expression b, "hello" is of type A& . B的超载是看不到的,因为第一个表达式b, "hello"的结果b, "hello"A&型。

So how to solve it? 那怎么解决呢? You can use a design pattern called CRTP ("Curiously Recurring Template Pattern") and turn your definitions of A and B into the following: 您可以使用名为CRTP的设计模式(“奇怪的重复模板模式”),并将AB定义转换为以下内容:

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

This way, the last line of the main() function in the example above will print what you expect to the standard output: 这样,上例中main()函数的最后一行将打印出您对标准输出的期望:

A::operator , (string)
B::operator , (double)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM