Cpp 操作符重载函数在模板里找不到的问题记录

主要内容

笔者此前在使用 dbg-macro 库的时候碰到了一个奇怪的问题, 就是操作符的重载函数无法被找到。

准确的说是被模板的 SFINAE 原则忽略了。

大致的情况是这样的:

  • 有一个结构体叫做ImVec2,它处于全局命名空间里面。 来自库imgui

  • 操作符的重载函数放在一个叫做sight的命名空间里面。

  • 1
    2
    3
    4
    5
    6
    7
    
    namespace sight {
      void test(){
        ImVec2 v;
        std::cout << v;   // ok
        dbg(v);           // fail
      }
    }
  • 大致代码如上面所述。

  • 解决办法是把 操作符重载函数和类型放入同一个命名空间, 比如把操作符的重载函数移到全局命名空间里面。

  • 在类型和操作符重载函数在不同的命名空间的时候, ADL 规则是无法生效的。 这里的问题是,似乎模板函数在生成的时候没有搜寻当前的命名空间, 但是正常函数调用的时候却会搜寻。

  • 可能是一个编译器 bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83035. 不过笔者使用的是 clang, 也是存在这个问题的。

dbg(v) 的解释

dbg-macro是一个用于方便输出调试信息的库, 特性大致如下

  • Easy to read, colorized output (colors auto-disable when the output is not an interactive terminal)
  • Prints file name, line number, function name and the original expression
  • Adds type information for the printed-out value
  • Specialized pretty-printers for containers, pointers, string literals, enums, std::optional, etc.
  • Can be used inside expressions (passing through the original value)
  • The dbg.h header issues a compiler warning when included (so you don’t forget to remove it).
  • Compatible and tested with C++11, C++14 and C++17.

dbg是一个宏, 最终拓展开的时候,会调用下面的函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace detail {
template <typename T>
using ostream_operator_t =
    decltype(std::declval<std::ostream&>() << std::declval<T>());

template <typename T>
struct has_ostream_operator : is_detected<ostream_operator_t, T> {};
}
// Specializations of "pretty_print"

template <typename T>
inline void pretty_print(std::ostream& stream, const T& value, std::true_type) {
  stream << value;
}

template <typename T>
inline void pretty_print(std::ostream&, const T&, std::false_type) {
  static_assert(detail::has_ostream_operator<const T&>::value,
                "Type does not support the << ostream operator");
}

template <typename T>
inline typename std::enable_if<!detail::is_container<const T&>::value &&
                                   !std::is_enum<T>::value,
                               bool>::type
pretty_print(std::ostream& stream, const T& value) {
  pretty_print(stream, value,
               typename detail::has_ostream_operator<const T&>::type{});
  return true;
}

完成代码可以在这里浏览: https://github.com/sharkdp/dbg-macro/blob/master/dbg.h

拓展阅读

0%