繁体   English   中英

模板函数返回void

[英]template function returning void

我的日志记录类中具有以下功能:

template<class T> 
inline T ErrLog(T ret, const char* Format, ...)
{
    va_list args; va_start( args, Format ); _vsnprintf(mypWorkBuffer, MaxLogLength, Format, args); va_end(args);
    // do some fancy logging with mypWorkBuffer
    return ret;
}

(mypWorkBuffer在其他地方定义)

这对我来说是一个非常方便的快捷方式,因为我可以记录一个错误并在一行中退出,这可以通过避免错误处理来使代码更具可读性:

int f(x) {
   if (x<0) return ErrLog(-1, "f error, %d too small", x);
   ...
}

(代替

int f(x) {
   if (x<0) {
      Log("f error, %d too small", x);
      return -1;
   }
   ...
}

我的问题是f返回void。 我想要做

void f(x) {
   if (x<0) return ErrLog(void, "f error, %d too small", x);
   ...
}

但这不能编译。

我想到了专业化,那就是添加:

inline void ErrLog(const char* Format, ...)
{
    va_list args; va_start( args, Format ); _vsnprintf(mypWorkBuffer, MaxLogLength, Format, args); va_end(args);
    // do some fancy logging with mypWorkBuffer
    return;
}

这可以让我做

return ErrLog("f error, %d too small", x);

但是我不确定返回char *的函数是否安全。 例如考虑:

char* f(x) {
   if (x<0) return ErrLog("error", "f error , %d too small", x);
   ...
}

我认为这将同时适合模板和专业。

有什么想法/更好的解决方案吗?

我认为仅使用逗号运算符会更加惯用且简单得多:

inline void Errlog(const char *format,...) { /* ... */ }

void f(int x) {
    if (x<0) return Errlog("f error...");
}

double g(double x) {
    if (x<0) return Errlog("x is negative..."),-1.0;
    else return sqrt(x);
}

逗号运算符的左手大小可以是一个void表达式。

这完全消除了使用模板函数的需要。

编辑:如果您真的不喜欢使用逗号...

如果您确实要使用功能接口,则有三个选择。 给定一个泛型函数(我在这里使用std::forward ,以便我们可以将其与引用等配合使用):

template <class T>
inline T &&ErrLog(T &&ret,const char *format,...) {
    /* logging ... */
    return std::forward<T>(ret);
}

您可以:

1)对void情况使用单独的功能。

void ErrLogV(const char *format,...) {
    /* logging ... */
}

void foo1(int x) {
    if (x<0) return ErrLogV("foo1 error");
}

2)具有特殊“标签”类型的重载:

static struct errlog_void_t {} errlog_void;

inline void ErrLog(errlog_void_t,const char *format,...) {
    /* logging ... */
}

void foo2(int x) {
    if (x<0) return ErrLog(errlog_void,"foo2 error");
}

3)或使用扔掉参数并将其强制转换为无效:

void foo3(int x) {
    // uses generic ErrLog():
    if (x<0) return (void)ErrLog(0,"foo3 error");
}

第二次编辑:为什么没有ret参数的版本无法正常工作

您可以使用ret安全地定义版本; 它不是模棱两可的,但是您不一定要使用它。 根本的问题是,在一种情况下,您将想要一种行为,而在另一种情况下,则需要另一种行为,但是函数调用参数将具有完全相同的类型。

考虑以下示例:

#include <cstdarg>
#include <cstdio>
#include <utility>

using namespace std;

template <typename T>
T &&foo(T &&x,const char *format,...) {
    puts("T-version");
    va_list va;
    va_start(va,format);
    vprintf(format,va);
    va_end(va);
    puts("");
    return forward<T>(x);
}

void foo(const char *format,...) {
    puts("void-version");
    va_list va;
    va_start(va,format);
    vprintf(format,va);
    va_end(va);
    puts("");
}

int main() {
    foo("I want the void overload: %s, %s","some string","some other string");
    foo("I want to return this string","I want the const char * overload: %s","some string");
}

这将编译! 但是只会调用foo的第一个版本。 编译器无法将您的意图与参数类型区分开。

为什么不是模棱两可?

两种版本的foo均可作为重载解决方案的候选者,但第一个版本将是更好的匹配。 您可以参考该过程详细说明 (特别是有关排名的部分),但是简而言之,当您有两个或多个const char *参数时,第一个foo版本的const char *第二个参数比第二个版本的省略号参数。

如果只有一个const char *参数,则返回空值的版本将胜过通用的版本,因为在其他条件相同的情况下,非模板重载比模板重载更受欢迎

foo("this will use the void-version");

简而言之,使用重载将进行编译,但会产生令人惊讶且难以调试的行为,并且无法处理void返回版本接受多个参数的情况。

对于void问题,请使用返回0的int特化,然后忽略它:

void f(x) {
   if (x<0) {
      ErrLog(0, "f error, %d too small", x);
      return;
   }
   ...
}

对于返回char *函数,只要返回的值是静态的,就可以放心使用(但是在使用C ++时,您还可以使用std::string ):

char* f(x) {
   static char err[] = "error";
   if (x<0) return ErrLog(err, "f error , %d too small", x);
   ...
}

编辑:

对于void部分,我无法想象如何避免阻塞(独立于模板问题):

void g(int x);
void f(int x) {
    if (x<0) return g(x); // 2 errors here : g returns void and f returns void
}

如果函数g返回void,则无论该函数是什么,都不能将其用作return语句的值。 无论如何,您不能使用return something; 在返回void的函数中。 我能想象的最好的是(但不是您要的):

void f(x) {
   if (x<0) ErrLog(0, "f error, %d too small", x);
   else {
      ...
   }
}

为else部分创建另一个块...

我建议您将模板专业化与用户定义的Void类型一起使用,以处理您的Void情况。 抱歉,这是C ++ 14,但是您应该能够轻松转换它,只需相应地更改返回类型

template<class T>
auto ErrLog(T ret, const char* Format, ...)
{
    return ret;
}

struct Void{ };

template<>
auto ErrLog(Void ret, const char* Format, ...)
{
    return;
}

int main()
{
    ErrLog(Void{}, "f error, %d too small");
    ErrLog(-1, "f error, %d too small");
}

另外,我没有通过可变参数模板Args,该解决方案更能向您展示这个想法。

暂无
暂无

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

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