运算符重载导致无效

Gem*_*lor 3 c++ c++11

我的代码中有一个情况,我想通过模板调用类定义的二元运算符重载,但第二个参数可能是 void 类型。可以写这个专业吗?

原因:

所以我有一个现有的宏化/模板包装,它可以帮助我记录函数的返回值。

它有点像这样:

#define Return  return DebugPlacement({__FILE__,__LINE__}) <<= 
Run Code Online (Sandbox Code Playgroud)

其中 DebugPlacement 是一个小点类,具有模板化重载operator <<=

struct DebugPlacement
{
   const char* path; int line;

   template <class Arg>
   const Arg& operator <<= (const Arg& arg)const
   {
       std::cerr << "DIAG: " << path << ":" << line << " returns " << arg << std::endl;
       return arg;
   }

};
Run Code Online (Sandbox Code Playgroud)

我们选择运算符 <<= 是因为它相当晦涩,并且行为有点像现有的流运算符,但相反。而且它的低优先级意味着几乎任何合理的表达式都可以写在 RHS 上。

另一个妥协:所有类型通常都很简单,因此 rval 的使用不是一个大问题。我确信我们最终会在这里看到完美转发的需要,但现在还不是。

它打印该值并返回它。我可以插入各种一般专业化,因为我发现我需要它们, - char* 等。不需要额外的括号。效果非常好,谢谢您的询问!

在很多情况下,它用在 API 存根中:

extern int MyFunction(int (arg1), int (arg2));
int MyFunctionStub(int (arg1), int (arg2))
{
  Return MyFunction(int (arg1), int (arg2));
}
Run Code Online (Sandbox Code Playgroud)

而这种存根行为反过来又被宏观化了。我知道——这是一种诅咒,也是一种祝福。不用说,我们的用例并不那么微不足道。存根也有工作要做,因此不能被消除或模板化。存根的名称在历史上也被固定为公共“C”API。

但如果返回类型为 void,一切都会崩溃!

看来善良的 c++ 大神认为以下内容是可以接受的,可能是因为模板包装器需要盲目转发返回类型:

extern void MyVFunction(int (arg1), int (arg2));
void MyVFunctionStub(int (arg1), int (arg2))
{
  return MyVFunction(int (arg1), int (arg2));
}
Run Code Online (Sandbox Code Playgroud)

但通过宏替换,我无法添加跟踪。他们不喜欢:

extern void MyVFunction(int (arg1), int (arg2));
void MyVFunctionStub(int (arg1), int (arg2))
{
  return DebugPlacement({__FILE__,__LINE__}) <<= MyVFunction(int (arg1), int (arg2));
}

Errors (at the call-site):
 error: no viable overloaded '<<='
 note: candidate template ignored: substitution failure [with T = void]: cannot form a reference to 'void'
Run Code Online (Sandbox Code Playgroud)

那么是否有某种形式的单词来声明需要右侧但类型为 void 的二元运算符的特化?目前我们仍然生活在c++11的土地上。难道我要等待后来的c++标准,还是标准化之神这次不眷顾我了?

当然,我也尝试过一些事情:

template <>
auto DebugPlacement::operator <<=(void& t)const -> void&
error: cannot form a reference to 'void'
Run Code Online (Sandbox Code Playgroud)

    void operator <<=(void t)const 
error: argument may not have 'void' type
Run Code Online (Sandbox Code Playgroud)

Max*_*kin 7

我的代码中有一个情况,我想通过模板调用类定义的二元运算符重载,但第二个参数可能是 void 类型。可以写这个专业吗?

您可以void使用内置的二元逗号运算符处理返回类型:

在逗号表达式中E1, E2,表达式E1被求值,其结果被丢弃(尽管如果它具有类类型,则直到包含的完整表达式结束时它才会被销毁),并且其副作用在表达式求值E2开始之前完成(请注意,用户定义的运算符不能保证排序)(C++17 之前)

当其右侧操作数为void逗号时,运算符的计算结果为void。逗号运算符只能对非空操作数重载。当任何操作数void仅调用内置逗号运算符时,operator,不考虑用户定义。

逗号运算符优先级最低,非常适合您的任务。

请注意,逗号符号 ( ,) 在不同的上下文中具有不同的含义

各种逗号分隔列表(例如函数参数列表 ( f(a, b, c)) 和初始化列表int a[] = {1, 2, 3})中的逗号不是逗号运算符。如果需要在此类上下文中使用逗号运算符,则必须将其放在括号中:f(a, (n++, n + b), c)

C++11 示例:

#include <iostream>
#include <utility>

struct ReturnValueLogger {
    char const* file_;
    int const line_;

    std::ostream& log_file_line() const {
        return std::clog << file_ << ":" << line_ << ' ';
    }

    ReturnValueLogger(ReturnValueLogger const&) = delete;

    ~ReturnValueLogger() {
        if(file_) // operator, overload is not called for void rhs.
            log_file_line() << "Return value is void\n";
    }

    template<class T>
    auto operator,(T&& rhs) -> decltype(std::forward<T>(rhs)) { // Not called for void rhs.
        log_file_line() << "Return value is " << rhs << '\n';
        file_ = 0;
        return std::forward<T>(rhs);
    }
};

#define Return return ReturnValueLogger{__FILE__,__LINE__},

int  f() { return 1; }
void g() {}

int  f2() { Return f(); } // Outputs "Return value is 1".
void g2() { Return g(); } // Outputs "Return value is void".

int main() {
    f2();
    g2();
}
Run Code Online (Sandbox Code Playgroud)

重载逗号运算符对于类似的宏也很有用,Throw可以在抛出站点处使用堆栈跟踪、文件和行信息来检测异常。