将指定的模板类型作为模板参数传递

Passing specified template type as template parameter

假设我有一些模板类型...

template <typename T> struct Foo {
    Foo(T t) {}
};

有没有办法将指定的 Foo 类型传递给函数,以便函数直接可见 T?

理想情况下我可以写出这样的东西...

Foo<int> foo = create<Foo<int>>();

我最接近的是

template <
    template <typename> typename TT,
    typename T,
    std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
    return Foo<T>(T());
}

然后将像

一样使用
Foo<int> foo = create<Foo, int>();

感谢您的帮助。

这种形式的模板模板参数只在C++17中被允许:

template < //           v---------- typename here not allowed
    template <typename> typename TT,
    typename T,
    std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
    return Foo<T>(T());
}

您必须替换 class 指出的 typename:

template < //           v---------- class allowed
    template <typename> class TT,
    typename T,
    std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
    return Foo<T>(T());
}

在 C++17 中,两者都编译并且是等价的。


要使语法 Foo<int> foo = create<Foo<int>>(); 有效,您只需执行以下操作:

template <typename T>
T create() {
    return T{};
}

如果你想限制可以发送什么类型,你必须创建一个类型特征:

// default case has no typedef
template<typename>
struct first_param {};

// when a template is sent, define the typedef `type` to be equal to T
template<template<typename> class TT, typename T>
struct first_param<TT<T>> {
    using type = T;
};

// template alias to omit `typename` everywhere we want to use the trait.
template<typename T>
using first_param_t = typename first_param<T>::type;

然后,使用你的特质:

template <
    typename T, 
    void_t<first_param_t<T>>* = nullptr
> //       ^---- if the typedef is not defined, it's a subtitution error.
T create() {
    return T(first_param_t<T>{});
}

您可以这样实施 void_t

template<typename...>
using void_t = void;

Live at Coliru

为什么不简单地使用标签调度,例如:

template <class>
struct tag { };

template <class T>
Foo<T> create(tag<Foo<T>>) {
    return Foo<T>(T());
}

//...

Foo<int> foo = create(tag<Foo<int>>{});

在 C++11 中

Demo

要点是有一个名为 create 的入口点函数,它可以实例化 create_helper 结构以创建正确的类型。

我们可以使用模板特化来创建我们的结构,这样我们就可以强制传递一个模板化的 class。

完整代码:

template<class T>
struct create_helper
{
    static_assert(sizeof(T) == 0, "Need to pass templated type to create");
};

template <class T, template<class> class TT>
struct create_helper<TT<T>>
{
   static TT<T> apply()
   {
       return {T{}};
   }
};

template<class T>
auto create() -> decltype(create_helper<T>::apply())
{
    return create_helper<T>::apply();
}

还有一个测试:

template<class T>
struct Foo
{
     Foo(T t){std::cout << "Constructed Foo with value " << t << std::endl;}
};
int main()
{ 
    Foo<int> foo = create<Foo<int>>();
}

输出:

Constructed Foo with value 0

一种简单的方法是直接在Foo中添加子类型信息:

template <typename T> struct Foo {
    using type = T;
    Foo(T t) {}
};

然后

template <typename FooT>
FooT create() {
    return FooT(typename FooT::type{});
}

如果需要,您可以添加 SFINAE:

template <typename FooT>
auto create()
-> decltype(FooT(typename FooT::type{}))
{
    return FooT(typename FooT::type{});
}

如果你真的想将函数限制为 Foo 独占,你必须在其上创建一个特征和 SFINAE。