如何仅通过其中一种类型来引用具有多个模板类型的 class?

How do I refer to a class with multiple template types by only one of the types?

假设我有一个 class Shape 声明:

template <typename T, typename U, typename V>
class Shape {
   T value;
   U input;
   V input2;

   ...
}

实际上,如果我创建一个 Shape 对象,它的类型将类似于 Shape<int, float, double> - 例如。

但是,如果我希望能够创建一个 Shape 对象并仍然为其提供不同类型的输入(例如 floatdouble),但我想要它的类型怎么办?成为 Shape<int>.

也就是说,在创建一个 Shape 对象之后,我希望调用者只关心它的值的类型,而不关心它的输入的类型。你建议我怎么做?

我了解到,如果不在顶部声明模板类型,我就无法存储模板成员变量。我也尝试过使用像这样的别名:

template <typename T, typename U, typename V>
using ShapeAlias<T> = Shape<T, U, V>

但这也不管用。你对我如何处理这件事有什么建议吗?

我正在考虑某种形式的继承,其中有一个基 class 只有一种类型,派生的 class 包含所有三种类型,但我想我应该在这里检查。

编辑:我需要第二种和第三种类型,因为用户可以将函数传递给 Shape 构造函数来计算 T 的值,它看起来像:

auto creator = [](U a, V b){
   return (something of type T)
}

我想将输入类型的值保留在 Shape class 中。

所以从客户的角度来看,他们的代码应该是这样的:

Shape<T> shapeA(Shape<T>(uValue, vValue, creator)

像现在这样,他们将不得不做:

Shape<T, U, V> shapeA(Shape<T, U, V>(uValue, vValue, creator))

您似乎在寻找 CTAD(class 模板参数推导)。它仅在调用者未指定任何模板参数时有效,因此必须添加一个间接层:

template <typename T>
struct ShapeWrap {
    template <typename U,typename V>
    struct Shape {
        T value;
        U input;
        V input2;
        Shape(const U& u,const V& v) : input(u),input2(v) {}
    };
};

来电者现在可以拨打:

auto s = ShapeWrap<int>::Shape(1.0,0.1f);

实例化ShapeWrap<int>::Shape<double,float>.


或者通过函数模板的模板参数推导:

template <typename T, typename U,typename V>
struct Shape {
    T value;
    U input;
    V input2;
    Shape(const U& u,const V& v) : input(u),input2(v) {}
};

template <typename T,typename U,typename V>
Shape<T,U,V> make_shape(const U& u, const V& v) {
    return {u,v};
}



int main() {
    auto s = make_shape<int>(1.0,0.1f);
}

如果创建者对象仅在构造函数本身中使用,但您不需要存储值供以后使用,则可以求助于模板化构造函数。如果您确实需要存储值,则需要指定包括所有类型参数在内的完整类型,如果用户必须自己命名类型。您可以以 auto 可用的方式设计 class。

备选方案 1:创建用于创建对象的函数。

这允许用户从左侧开始指定一些模板参数,但其余部分由编译器推导。

template<class Product, class CreatorType, class ...Args>
class Factory
{
public:
    Factory(CreatorType const& creator, Args...args)
        : m_creator(creator), m_arguments(args...)
    {
    }

    Product Create()
    {
        return std::apply(m_creator, m_arguments);
    }
private:
    CreatorType m_creator;
    std::tuple<Args...> m_arguments;
};

template<class Product, class CreatorType, class ...Args>
auto CreateFactory(CreatorType const& creator, Args ... args)
{
    return Factory<Product, CreatorType, Args...>(creator, args...);
}

int main() {

    auto creator = [](int value) -> int { return value + 1; };

    auto factory = CreateFactory<long>(creator, 41); // we're able do add a type conversion int -> long here

    std::cout << "The answer is " << factory.Create() << '\n';
}

备选方案 2:添加 class 模板参数推导 (CTAD) 指南

如果您同意根据调用创建者的结果自动推导 return 类型,您可以创建一个自动推导类型参数的 CTAD 指南,如果用户未指定任何模板参数。

template<class Product, class CreatorType, class ...Args>
class Factory
{
public:

    Factory(CreatorType const& creator, Args const&...args)
        : m_creator(creator), m_arguments(args...)
    {
    }

    Product Create()
    {
        return std::apply(m_creator, m_arguments);
    }
private:
    CreatorType m_creator;
    std::tuple<Args...> m_arguments;
};

// deduction guideline: the result of calling creator with the arguments is used as the first template parameter
template<class CreatorType, class ... Args>
Factory(CreatorType const&, Args const&...) -> Factory<decltype(std::apply(std::declval<CreatorType>(), std::declval<std::tuple<Args...>>())), CreatorType, Args...>;

int main() {
    auto creator = [](int value) -> int { return value + 1; };

    Factory factory(creator, 41); // type of factory automatically chosen to be Factory<int, ..., int>

    std::cout << "The answer is " << factory.Create() << '\n';
}