G++ and Clang++ agree that the following snippet is not valid C++:
template<int dim, int rank>
struct Tensor {};
template<int dim>
double InnerProduct(Tensor<dim, 1> const &, Tensor<dim, 1> const &)
{ return 0.0; }
template<int dim>
double DoubleInnerProduct(Tensor<dim, 2> const &, Tensor<dim, 2> const &)
{ return 0.0; }
template<int dim, int rank>
class Field
{
private:
static double Dot(Tensor<dim, rank> const &u, Tensor<dim, rank> const &v) requires (rank == 1)
{ return InnerProduct(u, v); }
static double Dot(Tensor<dim, rank> const &u, Tensor<dim, rank> const &v) requires (rank == 2)
{ return DoubleInnerProduct(u, v); }
};
template class Field<2, 1>;
template class Field<2, 2>;
The error message states that even the functions with unsatisfied constraints are instantiated:
error: no matching function for call to ‘DoubleInnerProduct(const Tensor<2, 1>&, const Tensor<2, 1>&)’
22 | { return DoubleInnerProduct(u, v); }
I can make it work in a number of ways (for example, declaring Dot
as templates with a default parameter equal to rank
to which the constraints are applied), but I expected it to work.
In general, shall I assume that template classes with member functions with constraints depending on their template parameters cannot be explicitly instantiated?
This is a bug of the c++20 experimental implementation of both GCC and Clang.
This is reported as GCC bug #77595.
In the c++20 paragraph [temp.explicit]/11:
An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members (not including members inherited from base classes and members that are templates) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, provided that the associated constraints, if any, of that member are satisfied by the template arguments of the explicit instantiation ([temp.constr.decl], [temp.constr.constr]), except as described below. [...]
According to this c++20 adendum "provided that the associated constraints, if any, of that member are satisfied" means that only one of the two Dot
overload shall be explicitly instantiated.
The bolded clause "except as described below" was there in the c++17 standard. It does apply to the first clause "An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members". The interpretation of this exception has not changed with c++20. Probably that the commitee overlooked that, grammaticaly, it could be correct to apply the exception to the c++20 adendum.
Below is the c++17 version of this paragraph:
An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members (not including members inherited from base classes and members that are templates) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, except as described below.
In this shorter sentence the meaning is clear.