I try to make a template function of two argument typenames a friend of a template class. But i cannot manage to make it link.
Consider following example. Note, that the code is extremely simplified.
#include <iostream>
template <typename T>
struct S;
template <typename U, typename T>
S<U> cast(S<T> const &); //PROBLEMATIC!
template <typename T>
std::ostream& operator<< (std::ostream& os, S<T> const & s); //no problem
template <typename T>
struct S
{
public:
S() = default;
~S() = default;
S( S const & ) = default;
S( S&& ) = default;
S(T val) : value{val} {}
template <typename U>
friend
S<U> cast(S<T> const &); //PROBLEMATIC!
friend
std::ostream& operator<< <T>(std::ostream& os, S<T> const & s); //no problem
private:
T value;
};
template <typename T>
inline std::ostream& operator<< (std::ostream& os, S<T> const & s) //no problem
{ return os << s.value; }
template <typename T, typename U>
S<U> cast(S<T> const & src) //problematic!
{return {static_cast<U>(src.value)};}
int main()
{
S<int> s1{ 10 };
auto s2{ cast<float>(s1) }; //linker error
std::cout << s1 << ' ' << s2;
return 0;
}
So, simply put, cast
is a glorified conversion function. It cannot be rewritten as a regular conversion function in original design.
Notice, that template operator<<
gets explicitly specialized, so there is no problem. However, cast
can only be partially specialized, which is (AFAIK) not allowed for functions.
The question is: How (if) I can achieve this without explicitly instantiating S<float>
?
I have tried removing forward declaration of cast
before S
. It did not help.
I have tried template <typename U> friend S<U> cast<U>(S<T> const &);
. It raises partial specialization not allowed
.
Exact linker errors:
<source>:26:10: error: function template partial specialization is not allowed
26 | S<U> cast<U>(S const &);
| ^ ~~~
1 error generated.
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/example-fd62b9.o: in function `main':
<source>:45:(.text+0x22): undefined reference to `S<float> cast<float>(S<int> const&)'
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
This is a working code
#include <iostream>
template <typename T>
struct S;
template <typename T>
std::ostream& operator<<(std::ostream& os, S<T> const& s); // no problem
template <typename T>
struct S {
public:
S(T val) : value{val} {}
template <typename U, typename V>
friend S<U> cast(S<V> const&);
friend std::ostream& operator<< <T>(std::ostream& os, S<T> const& s);
private:
T value;
};
template <typename T>
inline std::ostream& operator<<(std::ostream& os, S<T> const& s) {
return os << s.value;
}
template <typename U, typename T>
S<U> cast(S<T> const& src) {
return {static_cast<U>(src.value)};
}
int main() {
S<int> s1{10};
auto s2{cast<float>(s1)};
std::cout << s1 << ' ' << s2;
return 0;
}
https://godbolt.org/z/jTxrn96zz
cast
is unnecessary. template <typename U, typename V>
friend S<U> cast(S<V> const&);