ARTICLE AD BOX
The error is actually not about the assignment operator at all — it's about construction. The line:
cpp
PushConstant_Vec1 v = 4.5f;is copy-initialization, which requires a constructor that accepts a float, not an assignment operator. The compiler is looking for:
cpp
PushConstant_Vec1(float) // doesn't existThe operator= you defined would only be relevant for an already-constructed object, like:
cpp
PushConstant_Vec1 v; v = 4.5f; // this would work fineThe Inheritance Problem
Even if you added a converting constructor to the base class:
cpp
PushConstant(const T& value) : Value(value) {}...it still wouldn't work for PushConstant_Vec1 v = 4.5f; because the error message is telling you exactly why:
"Conversion requires a second user-defined-conversion operator or constructor"
C++ only allows one implicit user-defined conversion in a chain. To construct PushConstant_Vec1 from float, the compiler would need to:
Convert float → PushConstant<float, vec1> (user-defined conversion #1)
Convert PushConstant<float, vec1> → PushConstant_Vec1 (user-defined conversion #2)
That's two hops, which C++ explicitly forbids implicitly.
Solutions
Option 1 — Inherit the base constructor with using (simplest)
cpp
struct PushConstant_Vec1 : public PushConstant<float, PushConstantType::vec1> { using PushConstant::PushConstant; // pulls in base constructors directly };You still need a converting constructor in the base:
cpp
template<typename T, PushConstantType P> struct PushConstant { PushConstant() = default; PushConstant(const T& value) : Value(value) {} // add this PushConstant<T, P>& operator=(const T& value) { Value = value; return *this; } T Value {}; };Now PushConstant_Vec1 v = 4.5f; works because the inherited constructor is treated as belonging to the derived class directly — no two-hop conversion needed.
Option 2 — Explicit constructor in the derived class
If you want the derived class to be self-contained:
cpp
struct PushConstant_Vec1 : public PushConstant<float, PushConstantType::vec1> { PushConstant_Vec1(float value) { Value = value; } };More verbose but makes the intent very explicit.
Option 3 — Implicit conversion operator (alternative direction)
If you'd rather keep construction default and make the base class convertible, you can add an implicit conversion operator:
cpp
operator T() const { return Value; }But this doesn't help with v = 4.5f construction — it goes the wrong direction (PushConstant → T, not T → PushConstant).
Recommendation
Option 1 with using PushConstant::PushConstant is the idiomatic modern C++ solution — it keeps the derived structs as bare-bones as you want while the base class owns all the logic cleanly.
