2

我想为to_string(obj)我创建的每个对象类型提供一个函数。我找到了这个问题,应用了接受的答案,并且它有效。到目前为止,一切都很好。

然后我创建了一个新类型,但忘记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;
}

这里的问题是:

  • 我必须包装所有std::to_string() 重载
  • 我只会得到一个运行时错误,但我觉得这个问题可以而且应该被检测到广告编译时间
  • 我可能会增加一些开销,只在开发期间有用:也许我可以添加一些宏来在发布模式下停用所有这些,但它会增加更多工作

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

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

谢谢!

4

1 回答 1

1

接受的答案的想法是不同的:您将命名空间放在A 外部 notstd,然后使用 qualifiednotstd::to_string而不是 unqualified to_string。那是:

namespace notstd {
    // ...
}

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

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

现在,如果没有friend函数,您的代码将无法编译。而且,你只需要一个友元函数(take const A&),因为notstd::to_string(T&&)它不会出现在里面的重载集合中adl_helper::as_string(T&&)

A里面notstd的一切都搞砸了。您有无限递归问题,并且您需要两个朋友来处理这两种情况A以及const A存在notstd::to_string(T&&)候选人的情况:如果只定义了一个朋友,则该候选人在其中一种情况下是更好的匹配,因为const应该添加/删除限定符以调用朋友功能。

于 2020-06-01T20:11:04.723 回答