I have two classes:
template <typename Tp>
class Model;
template <typename TMp>
class Mesh;
I need Model
to contain Mesh
as a member, but with the following constraints:
If Model<float>
, the member should be Mesh<float>
. If Model<double>
, the member can be either Mesh<double>
or Mesh<float>
.
I am trying to use concepts to address this, and have so far written:
template <typename Tp, typename TMp>
concept validMesh = requires (Tp model, TMp mesh)
{
std::floating_point<Tp>&& std::floating_point<TMp> && (std::same_as<Tp,TMp> || std::same_as<TMp, float>);
};
template <typename TMp>
requires std::floating_point<TMp>
class Mesh {
public:
Mesh() = default;
};
template <typename Tp=float , typename TMp=Tp>
requires validMesh<Tp,TMp>
class Model {
public:
Model(Mesh<TMp> mesh) : mesh{ mesh } { };
private:
Mesh<TMp> mesh;
};
This works, and I can use it like:
Mesh mesh;
Model model(mesh);
But if I want to use Mesh<float>
and Model<double>
, I need to specify both template parameters when instantiating Model
, that is:
Mesh<float> mesh;
Model<double,float> model(mesh);
Why is it not capable of deducing TMp
? That is to say, why doesn't the following work:
Mesh<float> mesh;
Model<double> model(mesh);
Shouldn't the compiler know that the mesh being passed into the constructor is of type Mesh<float>
? And thus, shouldn't TMp
be deducible as float
? Perhaps I'm pursuing something that would be unreadable anyways, it was just my first attempt at implementing this, and I was surprised when it did not work.
Model model(mesh);
makes use of class template argument deduction (CTAD). Model<double> model(mesh);
does not.
CTAD, is either all or nothing. You cannot explicitly specify some template argument and have the other deduced. Though, you can fall back to function template argument deduction where you can have some specified explicitly and the other deduced.
Your code fails to compile (missing includes) hence I'll use a different example.
#include <iostream>
#include <type_traits>
template <typename T>
struct Foo {};
template <typename T,typename U>
struct Bar {};
template <typename T,typename U>
Bar<T,U> make_bar(const Foo<U>&) { return {}; }
int main() {
Foo<int> x;
auto b = make_bar<float>(x);
std::cout << std::is_same_v<decltype(b),Bar<float,int>>;
}