2

我有这段代码:

#include <iostream>

template<class T> void* ToPtr(T t) { return ToPtr((void*)t); }
void* ToPtr(void* i) { return i; }
void* ToPtr(int i) { return (void*)(long)(unsigned int)i; }

template<class T> int ToInt(T t) { return ToInt((void*)t); }
int ToInt(void* i) { return (int)(unsigned int)(long)i; }
int ToInt(int i) { return i; }

struct MyClass {
  template<class T>
  void* Find(T t) { return ToPtr(t); }

  template<class T>
  int FindInt(T t) { return ToInt(t); }
};

int main() {
  MyClass myClass;
  int myInt = 1;
  std::cout << &myClass << std::endl;
  std::cout << myInt << std::endl;
  std::cout << myClass.Find(&myClass) << std::endl;
  std::cout << myClass.Find(myInt) << std::endl;
  std::cout << myClass.FindInt(&myClass) << std::endl;
  std::cout << myClass.FindInt(myInt) << std::endl;
}

该程序在第一次调用 Find() 时崩溃,但我不知道为什么。我正在使用 GCC 6.2.0,它仅符合 C++14,否则我会使用 constexpr。我究竟做错了什么?

4

2 回答 2

3
template<class T> void* ToPtr(T t) { return ToPtr((void*)t); }

这自称。永远。

由于非模板优先于模板,因此这很容易修复:您只需将非模板放在首位,以便它们在上述重载范围内:

void* ToPtr(void* i) { return i; }
void* ToPtr(int i) { return (void*)(long)(unsigned int)i; }
template<class T> void* ToPtr(T t) { return ToPtr((void*)t); }

int ToInt(void* i) { return (int)(unsigned int)(long)i; }
int ToInt(int i) { return i; }
template<class T> int ToInt(T t) { return ToInt((void*)t); }

现场演示

当您的程序“崩溃”时,您应该在调试器中运行它。你会很清楚地看到堆栈溢出,因为数百个堆栈帧都会显示相同的递归调用。

于 2020-01-05T00:17:04.723 回答
0

这个功能

template<class T> void* ToPtr(T t) { return ToPtr((void*)t); }

无限期地称自己为……

ToPtr不是此函数模板内的依赖名称(因为强制转换void*永远不是依赖于类型的表达式[temp.dep.expr]/3),因此,在函数模板的定义点查找它,而不是在实例化的点。由于没有ToPtr在定义点声明其他重载,因此函数模板最终会调用自身。确保ToPtr在定义函数模板之前声明所有重载...

除此之外,您真的不应该将指针转换为unsigned intorint因为这些类型不能保证足够大以实际表示指针值。如果你真的必须这样做,请使用std::uintptr_t 或 std::intptr_t ……

于 2020-01-05T00:16:40.237 回答