I have a class template that takes an std::array
as parameter. I want to be able to convert objects of one template instance to another template instance. In order to have template deduction work and not clash with the move constructor, I tried to implement the conversion with a static member function template, which takes an object of another type and returns an object of the type the function is a member of. I also need to make the class template instances friends with each other, so that the conversion function can access private members of the passed object.
A minimal example for my problem looks like this:
#include <array>
#include <cstddef>
#include <utility>
template <size_t T1, std::array<int, T1> T2>
class A {
public:
template <size_t U1, std::array<int, U1> U2>
friend class A;
template <size_t U1, std::array<int, U1> U2>
static A fromOther(A<U1, U2>&& other);
private:
int x;
};
template <size_t T1, std::array<int, T1> T2>
template <size_t U1, std::array<int, U1> U2>
A<T1, T2> A<T1, T2>::fromOther(A<U1, U2>&& other) {
A a;
a.x = other.x;
return a;
}
int main() {
using B = A<2, {{1, 2}}>;
B b = B::fromOther(A<1, {{1}}>());
}
MSVC reports several errors, starting with:
8: error C3855: 'A' template parameter 'U2' is incompatible with the declaration 'T2'
If I change the type of the second template parameter to size_t
, the program compiles. If I instead make the member function non-template by changing the type of the function parameter to a specific instance of the class template, the program also compiles.
I have also tried changing the friend declaration to befriend the member function instead:
template <size_t U1, std::array<int, U1> U2>
template <size_t V1, std::array<int, V1> V2>
friend A<U1, U2> A<U1, U2>::fromOther(A<V1, V2>&&);
This does not produce an error in the friend declaration, but the private member variable is still inaccessable. All variants that I've tried do work with GCC, so I'm wondering if this is a bug in MSVC and how I can work around it.
I would bet msvc bug.
Possible workaround is to move the dependencies into requirements:
template <size_t T1, std::array T2>
requires (T1 == std::size(T2))
class A {
public:
template <size_t U1, std::array U2>
requires (U1 == std::size(U2))
friend class A;
// ..
};