2

I have this piece of code:

#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;
}

The program crashes in the first call to Find() but I'm not sure why. I'm using GCC 6.2.0, which is only C++14 compliant, otherwise I'd use constexpr. What am I doing wrong?

6
  • 2
    You should probably explain what you are trying to do here. Is the a reason to do 3 c-style casts in a row? Some kind of promotion trick? Commented Jan 5, 2020 at 0:11
  • What is your goal here? Commented Jan 5, 2020 at 0:15
  • What kind of "crash"? What actually happens? Commented Jan 5, 2020 at 0:15
  • 1
    Your template ToPtr will not be able to find the functions declared after it. You are calling the template version recursively to infinity. Commented Jan 5, 2020 at 0:16
  • My application has some legacy code that accepts both int and void* and stores them in void* data structures. Right now there's just a bunch of functions replicated for each version of the functions. I'm trying to reduce the list by using templates to generate the variants instead. Commented Jan 5, 2020 at 0:17

2 Answers 2

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

This calls itself. Forever.

Since non-templates are preferred over templates, this is really easy to fix: you just have to put the non-templates first so that they're in scope within the overload above:

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); }

(live demo)

When your program "crashes", you should be running it in a debugger. You'd have seen the stack overflow quite clearly, as hundreds of stack frames would all show the same recursive call.

Sign up to request clarification or add additional context in comments.

Comments

0

This function

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

calls itself indefinitely…

ToPtr is not a dependent name inside this function template (because a cast to void* is never a type-dependent expression [temp.dep.expr]/3), thus, it is looked up at the point of definition of the function template, not at the point of instantiation. Since none of the other overloads of ToPtr are declared at the point of definition, the function template ends up calling itself. Make sure that all overloads of ToPtr are declared before the definition of your function template…

Apart from that, you should really never convert pointers to unsigned int or int as these types are not guaranteed to be large enough to actually represent a pointer value. Use std::uintptr_t or std::intptr_t for that if you really must do it…

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.