Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 你能用C++;?_C++_Templates_Operators_C Preprocessor_Metaprogramming - Fatal编程技术网

C++ 你能用C++;?

C++ 你能用C++;?,c++,templates,operators,c-preprocessor,metaprogramming,C++,Templates,Operators,C Preprocessor,Metaprogramming,有没有可能创建一个自定义操作符,以便您可以执行类似的操作 if ("Hello, world!" contains "Hello") ... 注:这是一个与“是否……是个好主意”分开的问题;) 对!!(嗯,有点) 有几个公开可用的工具可以帮助您。两者都使用预处理器代码生成来创建实现自定义运算符的模板。这些运算符由一个或多个内置运算符和标识符组成 由于这些实际上不是自定义运算符,而只是运算符重载的技巧,因此需要注意以下几点: 宏是邪恶的。如果你犯了一个错误,编译器对于追踪问题几乎毫无用处 即使

有没有可能创建一个自定义操作符,以便您可以执行类似的操作

if ("Hello, world!" contains "Hello") ...
注:这是一个与“是否……是个好主意”分开的问题;)

对!!(嗯,有点) 有几个公开可用的工具可以帮助您。两者都使用预处理器代码生成来创建实现自定义运算符的模板。这些运算符由一个或多个内置运算符和标识符组成

由于这些实际上不是自定义运算符,而只是运算符重载的技巧,因此需要注意以下几点:

  • 宏是邪恶的。如果你犯了一个错误,编译器对于追踪问题几乎毫无用处
  • 即使宏正确,如果运算符的使用或操作的定义有错误,编译器也只会稍微有用一些
  • 必须使用有效标识符作为运算符的一部分。如果您想要一个更像符号的运算符,可以使用
    \uu
    o
    或类似的简单字母数字
当我为这个目的在自己的图书馆工作时(见下文),我遇到了这个项目。下面是创建
avg
运算符的示例:

#define avg BinaryOperatorDefinition(_op_avg, /)
DeclareBinaryOperator(_op_avg)
DeclareOperatorLeftType(_op_avg, /, double);
inline double _op_avg(double l, double r)
{
   return (l + r) / 2;
}
BindBinaryOperator(double, _op_avg, /, double, double)
bool a = true myOr false;
// a == true
一开始我对这个问题的看法是什么。下面是一个类似的例子:

template<typename T> class AvgOp { 
public: 
   T operator()(const T& left, const T& right) 
   {
      return (left + right) / 2; 
   }
};
IDOP_CREATE_LEFT_HANDED(<, _avg_, >, AvgOp)
#define avg <_avg_>
模板类AvgOp{
公众:
T运算符()(常数T和左、常数T和右)
{
返回(左+右)/2;
}
};
IDOP_创建_左手(,AvgOp)
#定义平均值
关键区别

  • CustomOperators支持后缀一元运算符
  • IdOp模板使用引用而不是指针来消除空闲存储的使用,并允许对操作进行完整的编译时评估
  • IdOp允许您轻松地为同一根标识符指定多个操作

,更准确一些,C++本身只支持创建现有操作的新重载,而不是创建新的运算符。有语言(例如,ML和它的大多数后代)允许你创建全新的操作符,但是C++不是其中之一。


从外观上看,(至少)另一个答案中提到的CustomOperators库也不完全支持自定义运算符。至少如果我读的东西是正确的,它(在内部)会将您的自定义运算符转换为现有运算符的重载。这使得事情变得更容易,但牺牲了一些灵活性——例如,当您在ML中创建一个新运算符时,您可以赋予它不同于任何内置运算符的优先级。

您的建议只不过是对以下内容的语法分析:

if( contains( "Hello, world!", "Hello" ) ...

事实上,在cstring和std::string中已经有一个函数可以实现这一点。这也许有点像回答“这是个好主意吗?”但不完全是;与其问“你为什么需要/想要?”

技术上说,不行。也就是说,你不能扩展
操作符+
操作符-
,等等的集合。但你在你的例子中提出的是另一个东西。您想知道是否有“contains”的定义使得
string literal“contains”string literal
是一个表达式,具有非平凡逻辑(
#define contains”“
是平凡的情况)


字符串文字X字符串文字
格式的表达式不多。这是因为字符串文字本身就是表达式。所以,您正在寻找一种形式为
expr X expr
的语言规则。其中有很多,但它们都是运算符的规则,而这些规则不适用于字符串。尽管有明显的实现,
“Hello”+“world”
不是有效的表达式。那么,X在
字符串文本X字符串文本中还能是什么呢?它本身不可能是一个表达式。它不能是typename、typedef名称或模板名称。它不能是函数名。它实际上只能是一个宏,而宏是仅存的命名实体。关于这一点,请参见“是(嗯,有点)”的答案。

Sander Stoks在中详细探讨了一种方法,允许您使用以下格式:

if ("Hello, world!" <contains> "Hello") ...
if(“你好,世界!”“你好”)。。。
本质上,您需要一个重载运算符“”的代理对象。代理完成所有工作;'“包含”只能是一个没有自己行为或数据的单例

// Not my code!
const struct contains_ {} contains;

template <typename T>
struct ContainsProxy
{
    ContainsProxy(const T& t): t_(t) {}
    const T& t_;
};

template <typename T>
ContainsProxy<T> operator<(const T& lhs, const contains_& rhs)
{
    return ContainsProxy<T>(lhs);
}

bool operator>(const ContainsProxy<Rect>& lhs, const Rect& rhs)
{
    return lhs.t_.left   <= rhs.left && 
           lhs.t_.top    <= rhs.top && 
       lhs.t_.right  >= rhs.right && 
       lhs.t_.bottom >= rhs.bottom;
}
//不是我的代码!
const struct contains_{}contains;
模板
结构ContainsProxy
{
ContainsProxy(const T&T):T_(T){}
康斯特T&T;
};
模板
ContainsProxy运算符(const ContainsProxy&lhs、const Rect&rhs)
{
返回左下角=右下角;
}

我创建了以下两个宏:

#define define const struct
#define operator(ReturnType, OperatorName, FirstOperandType, SecondOperandType) OperatorName ## _ {} OperatorName; template <typename T> struct OperatorName ## Proxy{public:OperatorName ## Proxy(const T& t) : t_(t){}const T& t_;static ReturnType _ ## OperatorName ## _(const FirstOperandType a, const SecondOperandType b);};template <typename T> OperatorName ## Proxy<T> operator<(const T& lhs, const OperatorName ## _& rhs){return OperatorName ## Proxy<T>(lhs);}ReturnType operator>(const OperatorName ## Proxy<FirstOperandType>& lhs, const SecondOperandType& rhs){return OperatorName ## Proxy<FirstOperandType>::_ ## OperatorName ## _(lhs.t_, rhs);}template <typename T> inline ReturnType OperatorName ## Proxy<T>::_ ## OperatorName ## _(const FirstOperandType a, const SecondOperandType b)
警告 虽然这是一个有趣的练习,但它只是演示了启用宏的预编译器有多糟糕。添加这样的自定义运算符很容易产生某种元语言。尽管我们知道C++设计得有多糟糕(大多数考虑到它最初是作为C的扩展集),但是我们不应该改变它。如果你不能使用标准C++,这是唯一的方法来保持代码可以被其他人理解,那么你应该切换到另一种语言,这使得你想做的事情变成你喜欢的方式。有数以千计的语言-不需要与C++混淆,使其不同。


简而言之:您不应该使用此代码。除非宏的使用方式与内联方法相同,否则应避免使用宏。

堆栈溢出是为问答格式设计的。为了避免将其关闭为“非真实问题”,我建议您重新格式化,以便在问题部分有一个问题,在回答部分有一个答案。需要注意的是:由于预处理阶段发生在编译之前,与这些自定义运算符相关的任何错误消息都可能很难与您编写的代码联系起来,因为
bool a = true myOr false;
// a == true