I'm working in C++20 but without concepts. I have a integer type
template<typename T>
class profile_integer;
for which I wish to define a multiplication operator on
friend constexpr profile_integer<T> operator*<T>(const profile_integer<T>& lhs, const profile_integer<T>& rhs);
Good so far, but now I want to be able to handle multiplication between different types and since this is a template I can't do implicit conversions; so fine, I can define another operator (using std::complex
as an example)
friend constexpr typename std::enable_if<! std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs);
Now, apologies for the return type; since V
could be profile_integer<T>
I needed this.
and I define my operators
template<typename T>
constexpr profile_integer<T> operator*(const profile_integer<T>& lhs, const profile_integer<T>& rhs)
{
profile_integer<T> x;
x.value = lhs.value * rhs.value;
return x;
}
template<typename T, typename V>
constexpr typename std::enable_if<!std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs)
{
profile_integer<T> x;
x.value = rhs.value * lhs;
return x;
}
But now I have a problem, for my second friend declaration I have the error
inline function 'operator*<int>' is not defined
For the first declaration I was able to fix this by declaring the friend operator*<T>
which I believe specialized on T. For the second declaration any of operator*<...>
with any combination of T
and V
produces the error
function template partial specialization is not allowed
I have a full copy of a minimal example at Compiler Explorer here. I can resolve this issue by defining the friend operator overloads inside the class definition however I'm wondering if there's a way to resolve this without doing that as I'd like to understand what's going on here.
The full example is
#include <map>
#include <type_traits>
template<typename T>
class profile_integer;
template<typename T>
constexpr profile_integer<T> operator*(const profile_integer<T>& lhs, const profile_integer<T>& rhs);
template<typename T, typename V>
constexpr typename std::enable_if<! std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs);
template<typename T>
class profile_integer
{
private:
T value;
public:
profile_integer() : value{0} {}
template<typename V>
constexpr profile_integer(V x) : value{x} {}
friend constexpr profile_integer<T> operator*<T>(const profile_integer<T>& lhs, const profile_integer<T>& rhs);
template<typename V>
friend constexpr typename std::enable_if<! std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs);
};
template<typename T>
constexpr profile_integer<T> operator*(const profile_integer<T>& lhs, const profile_integer<T>& rhs)
{
profile_integer<T> x;
x.value = lhs.value * rhs.value;
return x;
}
template<typename T, typename V>
constexpr typename std::enable_if<!std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs)
{
profile_integer<T> x;
x.value = rhs.value * lhs;
return x;
}
int main()
{
profile_integer<int> x;
x = x * x;
x = 2*x;
return 0;
}
Edit: The C++20 standard 13.9.2(c) notes that constexpr
may not be used with explicit template specialization. Removing the constexpr
allows the code to compile but linking still fails when operator*(...)
does not include the <>
. Use of <>
still results in a partial template specialization error.
So after digging through the standard and not coming up with anything I tried this
I define a new function
template<typename T, typename V>
profile_integer<T> mul(const V& lhs, const profile_integer<T>& rhs)
{
return lhs * rhs.get_value();
}
I can call this function
int main()
{
profile_integer<int> x;
x = x * x;
x = mul(2, x);
return 0;
}
And this works
Now if I simply declare the function a friend in the class declaration
template<typename V>
friend profile_integer<T> mul(const V& lhs, const profile_integer<T>& rhs);
This compiles but fails to link.
I'm working in C++20 but without concepts. I have a integer type
template<typename T>
class profile_integer;
for which I wish to define a multiplication operator on
friend constexpr profile_integer<T> operator*<T>(const profile_integer<T>& lhs, const profile_integer<T>& rhs);
Good so far, but now I want to be able to handle multiplication between different types and since this is a template I can't do implicit conversions; so fine, I can define another operator (using std::complex
as an example)
friend constexpr typename std::enable_if<! std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs);
Now, apologies for the return type; since V
could be profile_integer<T>
I needed this.
and I define my operators
template<typename T>
constexpr profile_integer<T> operator*(const profile_integer<T>& lhs, const profile_integer<T>& rhs)
{
profile_integer<T> x;
x.value = lhs.value * rhs.value;
return x;
}
template<typename T, typename V>
constexpr typename std::enable_if<!std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs)
{
profile_integer<T> x;
x.value = rhs.value * lhs;
return x;
}
But now I have a problem, for my second friend declaration I have the error
inline function 'operator*<int>' is not defined
For the first declaration I was able to fix this by declaring the friend operator*<T>
which I believe specialized on T. For the second declaration any of operator*<...>
with any combination of T
and V
produces the error
function template partial specialization is not allowed
I have a full copy of a minimal example at Compiler Explorer here. I can resolve this issue by defining the friend operator overloads inside the class definition however I'm wondering if there's a way to resolve this without doing that as I'd like to understand what's going on here.
The full example is
#include <map>
#include <type_traits>
template<typename T>
class profile_integer;
template<typename T>
constexpr profile_integer<T> operator*(const profile_integer<T>& lhs, const profile_integer<T>& rhs);
template<typename T, typename V>
constexpr typename std::enable_if<! std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs);
template<typename T>
class profile_integer
{
private:
T value;
public:
profile_integer() : value{0} {}
template<typename V>
constexpr profile_integer(V x) : value{x} {}
friend constexpr profile_integer<T> operator*<T>(const profile_integer<T>& lhs, const profile_integer<T>& rhs);
template<typename V>
friend constexpr typename std::enable_if<! std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs);
};
template<typename T>
constexpr profile_integer<T> operator*(const profile_integer<T>& lhs, const profile_integer<T>& rhs)
{
profile_integer<T> x;
x.value = lhs.value * rhs.value;
return x;
}
template<typename T, typename V>
constexpr typename std::enable_if<!std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs)
{
profile_integer<T> x;
x.value = rhs.value * lhs;
return x;
}
int main()
{
profile_integer<int> x;
x = x * x;
x = 2*x;
return 0;
}
Edit: The C++20 standard 13.9.2(c) notes that constexpr
may not be used with explicit template specialization. Removing the constexpr
allows the code to compile but linking still fails when operator*(...)
does not include the <>
. Use of <>
still results in a partial template specialization error.
So after digging through the standard and not coming up with anything I tried this
I define a new function
template<typename T, typename V>
profile_integer<T> mul(const V& lhs, const profile_integer<T>& rhs)
{
return lhs * rhs.get_value();
}
I can call this function
int main()
{
profile_integer<int> x;
x = x * x;
x = mul(2, x);
return 0;
}
And this works
Now if I simply declare the function a friend in the class declaration
template<typename V>
friend profile_integer<T> mul(const V& lhs, const profile_integer<T>& rhs);
This compiles but fails to link.
Share edited Mar 8 at 20:21 Michael Conlen asked Mar 7 at 21:46 Michael ConlenMichael Conlen 2,1383 gold badges18 silver badges20 bronze badges 2 |2 Answers
Reset to default 2Your operator is a template with two template arguments, hence the friend declaration reads:
template<typename U,typename V>
friend constexpr typename std::enable_if_t<!std::is_same_v<profile_integer<U>, V>, profile_integer<U>> operator*(const V&, const profile_integer<U>&);
Note the use of the helper template _v
and _t
for less verbosity.
Live Demo
In your code there are 2 distinct template arguments that are both called T
. You want them to be equal for the friend operator. You want to befriend only a partial specialization of that U == T
and V
is any type. I admit, I don't know if this is possible. You cannot partially specialize a function template.
You can wrap the implementation in a class template and befriend that. At this point, I am not sure anymore if it's an advantage to not directly define the operator inline with the friend declaration. However, this is how it can be done:
#include <cstdint>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <type_traits>
template<typename T>
class profile_integer;
template<typename T>
constexpr profile_integer<T> operator*(const profile_integer<T>&, const profile_integer<T>&);
template<typename T, typename V>
constexpr typename std::enable_if<! std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs);
template <typename T>
struct mult {
template <typename V> constexpr typename std::enable_if<! std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type m(const V& lhs, const profile_integer<T>& rhs) {
profile_integer<T> x;
x.value = rhs.value * lhs;
return x;
}
};
template<typename T>
class profile_integer
{
private:
T value;
public:
profile_integer() : value{0} {}
template<typename V>
constexpr profile_integer(V x) : value{x} {}
friend constexpr profile_integer<T> operator*<T>(const profile_integer<T>& lhs, const profile_integer<T>& rhs);
friend mult<T>;
};
template<typename T>
constexpr profile_integer<T> operator*(const profile_integer<T>& lhs, const profile_integer<T>& rhs)
{
profile_integer<T> x;
x.value = lhs.value * rhs.value;
return x;
}
template<typename T, typename V>
constexpr typename std::enable_if<!std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs)
{
return mult<T>{}.m(lhs,rhs);
}
int main()
{
profile_integer<int> x;
x = x * x;
x = 2*x;
return 0;
}
When you declare
template<typename T>
class profile_integer;
and have
template<typename V>
friend constexpr typename std::enable_if<! std::is_same<profile_integer<T>, V>::value, profile_integer<T>>::type operator*(const V& lhs, const profile_integer<T>& rhs);
inside of of the class definition and you instantiate a profile_integer<T>
for some concrete type T
that declares a friend function operator*<V>
rather than operator*<T, V>
. So when we define an out of line function on <T, V>
that function is not a friend. This is because template parameters are part of the function signature.
This is the reason for the code compiling and linking without the friend declaration; it finds the defined function. When you add the friend declaration the overload resolution picks the friend declaration but that's not what's defined.
So the answer is that we can't do what was asked for (but now we know why). The options are to provide a specialization, define it inline or provide a public interface so that the operator need not be a friend.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744908756a4600417.html
2*x
but notx*2
? When you multiplyprofile_integer<int>
byprofile_integer<long>
, what is the return type and why? – n. m. could be an AI Commented Mar 7 at 22:32