7

I have a question about C++ templates. The following codes segfaults.

template <typename T1, typename T2>
inline T1 const& max(T1 const &a, T2 const &b) {
    return a < b ? b : a;
}
int main() {
    std::cout << max(4.9, 4) << std::endl;
}

However, remove the & and it does the right thing.

template<typename T1, typename T2>
inline T1 const max(T1 const &a, T2 const &b) {
    return a < b ? b : a;
}
int main() {
    std::cout << max(4.9, 4) << std::endl;
}

Furthermore, just use T instead of T1 and T2 and it works just fine.

template<typename T>
inline T const& max(T const &a, T const &b) {
    return a < b ? b : a;
} 

int main() {
    std::cout << max(4, 5) << std::endl;
}

What am I doing wrong here ?

  • Please mention what compiler, including version number, you are using. If it's GCC, have you compiled with `-Wall`? There may be a warning you are ignoring that is causing undefined behaviour. – Ken Y-N Aug 25 '17 at 02:52
  • The compiler should tell you the max() call returns reference to temporary. – tristan Aug 25 '17 at 02:54
  • 1
    The first two code snippets are identical? – asimes Aug 25 '17 at 03:03

2 Answers2

10

You should always compile with warnings turned on, see https://wandbox.org/permlink/KkhFOJw6QNJ7rv7J. If you had the warning flags on, the compiler would have helped you out and told you what you are doing wrong.

What is happening here is a promotion (see Return type of '?:' (ternary conditional operator)), you are executing a ternary expression on an int and a double. Doing that results in a temporary promoted double. And returning a reference to a temporary and referencing that after the function where its lifetime is bound returns is undefined behavior.

Curious
  • 20,870
  • 8
  • 61
  • 146
1

In the two first, you are returning a reference to a temporary.

Why? Well, your function is returning the type T1, but you will have to convert one of the types you send. The conversion creates a temporary of the right type. Then you're returning it.

Since the temporary dies, the returned reference is bound to a dead object.

You already found a fix. You either not returning a reference, or you either take parameters of the same type.

If you look at the standard implementation, it chose the second solution: taking parameters of the same type.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141