想知道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
和值,此方法允许执行以下操作:
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,
(我正在处理我的版本中的尾随逗号,为了缩短示例,省略了这部分),here is the code。
问题是指令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,
的结尾。这就像忽略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 ,
,但在同一名称空间上添加一个空闲运算符可以修复编译错误:
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)
会被调用,所以我现在要求一些建议:
Instruction<SQL_ORDER_BY>
实例未附加到查询字符串?ORDER
值未调用专业化提供的Fields &operator ,(const ORDER)
。operator ,
有Instruction<SQL_ORDER_BY>
个实例?PS:所有这些努力都是为了自学成分,这段代码的几乎零行将最终出现在生产代码中,所以请避免关于使用库或代码实用程序的注释,
感谢。
编辑:
有人删除了他的回答,建议在专业化中添加using Fields::operator std::string;
和using Fields::operator,;
行,修复忽略ORDER_BY
问题。
答案 0 :(得分:1)
问题是由于,
的{{1}}子类中Instruction<SQL_ORDER_BY>
运算符的重载是隐藏超类的重载运算符。这就是函数调用解析在C ++中的工作方式:名称查找首先发生,一旦找到某个名称空间中的一组名称就停止;然后,执行重载决策。
问题在this related article by Herb Sutter中解释。这篇文章与您的问题并不完全相关,但包含了解决方案。特别是,请查看“示例2a ”。
您必须使用Fields
指令将using
基类的运算符重载导入到派生类的范围内,因此Field
中,
的重载超过了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的设计模式(“奇怪的重复模板模式”),并将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)