缺少用户定义的to_string()的编译时检测

问题描述 投票:2回答:1

我想为我创建的每种对象类型提供to_string(obj)函数。我找到了this question,应用了accepted answer,它可以正常工作。到目前为止一切顺利。

然后我创建了一个新类型,但是忘了为此写一个to_string()(或者更好:我无意中使ADL无法访问它)。问题是:我的程序仍然编译良好,并且在运行时我得到了一个晦涩的堆栈溢出(TM)。

是否有办法获取合理的错误消息?

这里有一个小程序来演示这个问题:notstd::to_string()notstd::adl_helper::as_string()之间的无限递归。

#include <iostream>
#include <string>

namespace notstd {
  namespace adl_helper {
    using std::to_string;

    template<class T>
    std::string as_string( T&& t ) {
      return to_string( std::forward<T>(t) );
    }
  }
  template<class T>
  std::string to_string( T&& t ) {
    std::cout << "called" << std::endl; // <-- this is to show what's going on
    return adl_helper::as_string(std::forward<T>(t));
  }

  class A {
    /* both versions are needed, or the perfect forwarding candidate will
     * always be chosen by the compiler in case of a non-perfect match */
    //friend std::string to_string(A &a) { return std::string("a"); }
    //friend std::string to_string(const A &a) { return std::string("a"); }
  };
}


int main(int argc, char** argv) {

  notstd::A a;

  std::cout << to_string(a) << std::endl;
}

我尝试创建一个接受另一个参数的包装器函数,以用于执行抗递归检查,如下所示:

#include <iostream>
#include <string>
#include <cassert>

namespace notstd {
  namespace wrap_std {
    std::string to_string(double v, bool) { return std::to_string(v); }
    /* .... etc.....  */
  }

  namespace adl_helper {
    using wrap_std::to_string;

    template<class T>
    std::string as_string( T&& t ) {
      return to_string( std::forward<T>(t), true );
    }
  }
  template<class T>
  std::string to_string( T&& t, bool recurring = false ) {
    std::cout << "called" << std::endl;
    assert(!recurring);
    return adl_helper::as_string(std::forward<T>(t));
  }

  class A {
    /* both versions are needed, or the perfect forwarding candidate will
     * always be chosen by the compiler in case of a non-perfect match */
    //friend std::string to_string(A &a) { return std::string("A"); }
    //friend std::string to_string(const A &a) { return std::string("A"); }
  };
}


int main(int argc, char** argv) {

  notstd::A a;

  std::cout << to_string(a) << std::endl;
}

这里的问题是:

  • 我必须包装all std :: to_string()重载
  • 我只会收到运行时错误,但我认为可以并且应该在广告编译时检测到该问题
  • 我可能会增加一些开销,仅在开发过程中有用:也许我可以添加一些宏以在发布模式下停用所有这些功能,但这会增加更多的工作

也许我可以使用模板来包装std::to_string()并为我的类型创建特化...这将是一个完全不同的野兽,但是如果没有合适的特化,至少它将提供编译时错误。同样,我将不得不包装所有std::to_string()重载,而且我可能不得不(几乎)忘记ADL,至少要等到所有编译器都支持c ++ 20为止,如果我理解得很好。

有人有更好的解决方案吗?

谢谢!

c++ c++11 tostring argument-dependent-lookup infinite-recursion
1个回答
0
投票

接受的answer的概念不同:您放置A outside notstd命名空间,然后使用合格的notstd::to_string而不是不合格的to_string。那是:

namespace notstd {
    // ...
}

class A {
    friend std::string to_string(const A&);
};

A a;
std::cout << notstd::to_string(a);

现在,如果没有friend函数,您的代码将无法编译。而且,您只需要一个朋友功能(取const A&),因为notstd::to_string(T&&)内部的重载设置中不会出现adl_helper::as_string(T&&)

A放入notstd内部将所有螺丝拧紧。您有无限递归问题,并且在有A个候选者的情况下需要两个朋友来处理const Anotstd::to_string(T&&)情况:如果仅定义一个朋友,则在以下情况之一中,该候选者是更好的匹配项,因为[ const应该添加/删除限定符以调用好友功能。

© www.soinside.com 2019 - 2024. All rights reserved.