【问题标题】:Explicitly specify template template types显式指定模板模板类型
【发布时间】:2021-05-17 19:30:43
【问题描述】:

我正在学习模板。如果我混淆了模板/模板类型/模板参数的概念,请纠正我。

我正在尝试编写一个模板函数来创建一个对象并返回它。对象的类型来自必须显式指定的模板参数。

result = createObject<ObjectType>();

这个对象虽然应该是一个模板。以容器为例。并且该函数应该知道对象的类型及其模板参数。例如:

result = createObject<Container<ElementType>>();

我已经尝试使用模板模板参数来解决它:

template <template<class> class ContainerType, class ElementType>
auto createObject()
{
    ContainerType<ElementType> result;

    //do stuff...

    return result;
}

//...

template<typename T>
struct Vector{};

//...

//const auto random_vec = createObject<Vector<float>>(); // ERROR.
  const auto random_vec = createObject<Vector, float>();

第二种情况有效,第一种无效。上面写着candidate template ignored: invalid explicitly-specified argument for template parameter 'ContainerType'

是否有可能让它像第一种情况一样工作?给它Vector&lt;float&gt; 之类的东西,它可以推断ContainerTypeVectorElementTypefloat?是否可以重载或专门化此功能,以便它以不同的方式处理某些类型的容器?我应该使用概念吗?

【问题讨论】:

  • 不是“createObject()”只是一个函数而不是一个类,结构吗?缺少分号。
  • 所以createObject需要区分要创建的类型是模板的特化而不是像int这样的简单类型的情况?
  • @DavisHerring 是的。
  • 既然您要求更正:“模板函数”是过时的术语。既然不是一种函数而是一种模板,那就用“函数模板”(虽然更一般的“模板化函数”也有意义)。

标签: c++ templates c++-concepts


【解决方案1】:

像这样进行分解的常用方法是通过部分特化,这需要一个帮助类模板:

namespace detail {
template<class> struct create;  // undefined
template<template<class T> class C,class T>
struct create<C<T>> {
  static C<T> make() {/* … */}
};
}

template<class T>
T createObject() {return detail::create<T>::make();}

如果您想支持一般情况,可以定义主模板,并且可以为其他类型的模板添加其他特化,例如std::array

【讨论】:

  • 谢谢!到目前为止,这有效。是否需要结构,因为函数不允许部分特化?如何为Vector&lt;T&gt;std::array&lt;T&gt; 添加专业化?我知道主要类型是Vector,元素类型是T的情况。
  • @Newline:对:函数模板允许重载,这有时非常相似,但在这种情况下根本不起作用(没有标签类型的恶作剧)。部分特化不必在参数列表中使用C;你可以把Vector放在那里(完全省略C)。 std::arraystd::map 当然需要 more 模板参数,但它们的工作方式类似。
  • 如果我没有在半夜写下我的答案,我的答案就是这样的 :-) 你得到了我的投票。
【解决方案2】:

您可以创建一个类型特征来检查该类型是否从模板实例化:

#include <type_traits>

// trait to check if the type is instantiated from a template
template<typename T>
struct is_template_instance_type : std::false_type {};

template<template<class,class...> class C, class T, class... Rest>
struct is_template_instance_type<C<T,Rest...>> : std::true_type {
    using class_type = C<T,Rest...>;
    using value_type = T;
    // using rest_types = std::tuple<Rest...>; 
};

// Helper variable template - if needed for something later
template<class T>
inline constexpr bool is_template_instance_type_v = is_template_instance_type<T>::value;

然后您可以添加重载:

template<class T, class C = is_template_instance_type<T>, class U = typename C::class_type>
auto createObject() {
    U result;
    // typename C::value_type x; // if you need the value type
    
    
    return result;
}

template<template<class,class...> class C, class T, class... Rest>
auto createObject() {
    return createObject< C<T,Rest...> >();
}

然后它将与Vector&lt;float&gt;Vector, float 一起使用,但不适用于float

Demo

【讨论】:

  • 谢谢!我需要时间来完全理解这一点。
  • @Newline 不客气。我个人更喜欢 Davis Herring 的答案,尽管我的答案中的类型特征可能有用 - 对于其他东西。 :)
【解决方案3】:

你可以这样简单:

template<typename T, typename V = typename T::value_type>
T createObject() 
{
    T t {}; // T will be e.g std::vector<int>
    V v {}; // V will be int

    // do work...
    t.push_back(v++);
    t.push_back(v++);
    // ...work done

    return t;
}

你可以这样使用它:

int main ()
{
    auto obj1 = createObject<std::vector<int>>();
    auto obj2 = createObject<std::list<double>>();

    return 0;
}

【讨论】:

  • 这意味着我必须在我的Vector 和其他容器中添加一个value_type 定义,这是可行的。虽然我想对Vector&lt;V&gt; 进行专业化,同时也有一个通用案例T&lt;V&gt;
猜你喜欢
  • 1970-01-01
  • 2019-03-23
  • 1970-01-01
  • 1970-01-01
  • 2020-10-20
  • 1970-01-01
  • 2011-03-17
  • 1970-01-01
  • 2011-06-27
相关资源
最近更新 更多