【发布时间】:2013-03-15 23:05:15
【问题描述】:
因此,多亏了 C++11,现在可以结合宏、用户定义的文字、lambdas 等来创建最接近“语法糖”的东西。一个例子是
if (A contains B)
当然这很容易。
cout <<("hello"_s contains "ello"_s)<<endl;
表达式转换为布尔值,其中 contains 是一个自定义结构,它将左侧和右侧作为参数。结构当然会重载 operator+ 以首先获取自定义字符串文字,然后返回自身,然后是结构本身的 operator+。
struct contains_struct {
string lhs;
string rhs;
void set_lhs(string lhs) { this->lhs = lhs; }
void set_rhs(string rhs) { this->rhs = rhs; }
operator bool() const {
return string::npos != lhs.find(rhs);
}
} contains_obj;
contains_struct& operator+(const string& lhs, const contains_struct& rhs) {
contains_obj.set_lhs(lhs);
return contains_obj;
}
contains_struct& operator+(const contains_struct& lhs, const string& rhs) {
contains_obj.set_rhs(rhs);
return contains_obj;
}
#define contains +contains_obj+
现在我决定要走得更远。怎么样
(x in a) perform cube
这不是列表推导,但它是一个很好的例子,对吧?一开始我说,好吧,我必须去stackoverflow询问自定义运算符的优先级,但是把它放在括号里是直截了当的,因为他们头脑正常的人不会使用我的代码。相反,我扩展了我的另一个示例,并将 'in' 和 'perform' 作为自定义结构,就像 'contains' 一样。
您可以更进一步并对其进行模板化,以便 x 可以是任何数字索引,而 a 可以是任何容器,但为简单起见,我将 x 保留为整数,将 a 保留为整数向量。现在到目前为止,它实际上并没有将局部变量 x 作为参数,而是在 operator string() 函数中本地使用它。
为了简化,我将表达式的结果存储在一个字符串中,就像这样
operator string() const {
string s = "";
for (int x : lhs.rhs)
s += to_string(rhs(x)) + string("\n");
return s;
}
感谢另一个问题:Overloading assignment operator for type deduction
我意识到将其作为作业返回的一个实际用途如下:
struct result_struct {
vector<int> results;
result_struct(vector<int> results) { this->results = results; }
};
...
operator result_struct() const {
vector<int> tmp;
for (int x : lhs.rhs)
tmp.push_back(rhs(x));
return result_struct(tmp);
}
...
result_struct result_2 = (x in a) perform cube;
for (int x : result_2.results)
cout <<x<<endl;
感谢milleniumbug's answer,我可以做到:
struct for_obj
{
int _lhs;
std::vector<int> _rhs;
for_obj(int lhs, std::vector<int> rhs)
: _lhs(lhs), _rhs(rhs) { }
};
INFIX_OPERATOR(for_obj, in_op, int, std::vector<int>)
{
return for_obj(lhs(), rhs());
}
#define in + in_op() +
INFIX_OPERATOR(int, perform_op, for_obj, std::function<int(int)>)
{
for (int i = 0; i < lhs()._rhs.size(); i++)
rhs()(lhs()._rhs[i]);
return 0;
}
#define perform + perform_op() +
有两个警告。首先,我返回一个 int 以便我可以将它分配给一个虚拟变量以使其执行。我总是可以做我以前做过的 result_struct 事情,或者返回一个 std::function 对象来自己调用它,但我会重复自己。另一个需要注意的是,由于宏中有太多 const,您无法修改 lhs(这不允许您指定迭代器)。
考虑到所有因素,以下工作按预期工作。
int x = 0;
std::vector<int> nums = { 1, 2, 3 };
auto cube = [] (int x)
{
std::cout << x * x * x << std::endl;
return x * x * x;
};
int i = (x in nums) perform cube;
新版本
class PerformObj {
int counter;
public:
PerformObj() : counter(0) { }
~PerformObj() { }
InObj lhs;
std::function<int(int)> rhs;
operator int() const {
return rhs(lhs.rhs[counter]);
}
} performobj;
#define perform + performobj +
PerformObj& operator+(const InObj& lhs, PerformObj& rhs) {
rhs.lhs = lhs;
return rhs;
}
PerformObj& operator+(PerformObj& lhs, const std::function<int(int)>& rhs) {
lhs.rhs = rhs;
return lhs;
}
int main()
{
std::vector<int> nums = {1,2,3};
int x = 0;
auto cube = [] (int n) {
return n * n * n;
};
std::cout << x in nums perform cube << std::endl;
}
explicit operator std::vector<int>() const {
std::vector<int> temp;
for (int i = 0; i < lhs.rhs.size(); i++) {
temp.push_back(rhs(lhs.rhs[i]));
}
return temp;
}
int y = 0;
std::cout << y in static_cast<std::vector<int>>(x in nums perform cube) perform std::function<int(int)>([] (int i) -> int {
return i;
}) << std::endl;
我应该让它代替中缀运算符,而是使用后缀运算符,例如"String literal"s.contains "Other string literal"s,还是使用函数样式,"String literal"s.contains("Other string literal"s)?
我将如何改进我的代码以使其更具可扩展性?就像现在一样,污染非常严重。有没有更好/更通用/不那么笨重的方法来做到这一点?例如,概括表达式以便我不需要定义语句或重用代码。
【问题讨论】:
-
这是一种很好的混淆技术,但绝对应避免使用。它使懂 C++ 的人无法阅读您的代码。
-
您似乎在重新发明表达式模板……查找该术语,您应该会发现很多。但至于在语言中引入新的中缀运算符,这是个坏主意。坚持使用函数调用符号,否则会失去人气。
-
我赞成这个,因为它既有趣又整洁,但我绝对不提倡在实际代码中实际使用它。
-
可怕的破解这个。如果您想要一种脚本语言,请使用一种。
-
不管怎样,这在 C++11 之前就已经成为可能。见:stackoverflow.com/questions/1515399/…
标签: c++ syntactic-sugar