【问题标题】:Un-specializing C++ template parameter非特化 C++ 模板参数
【发布时间】:2018-08-03 13:01:35
【问题描述】:

基本上我想做的如下。假设我们有一个模板成员函数 foo

template <typename T>
T SomeClass::foo();

不知何故,用户将 ma​​p 作为模板参数传递:

foo<map<string, int>>();

我在这里要做的是,在定义函数 foo 时,获取内部类型,stringint。我尝试了很多猜测来取消专业化论点,但无济于事。

template <map<typename K, typename V>>
map<K, V> SomeClass::foo();  // absolutely illegal

我考虑过使用部分特化,但它不起作用,因为 foo 是类成员函数。

【问题讨论】:

  • 你的“绝对非法”写错了。你可以做template &lt;typename K,typename V&gt; map&lt;K,V&gt; SomeClass:foo();
  • T::key_type/T::mapped_type ?
  • 您希望T 是任何类型,还是只有std::map。如果您没有专业,您对foo&lt;int&gt; 有什么期望?
  • @Jarod42 是的,我希望 T 是任何类型,尽管我会考虑一组有限的容器,例如 map、vector..

标签: c++ templates


【解决方案1】:

如果您想要从模板中获取内部类型的通用方式,您可以使用显式特化:

template <typename T>
struct unpack;

template <template <typename...> class C, typename A, typename B>
struct unpack<C<A, B>>
{
    using first  = A;
    using second = B;
};

用法:

static_assert(std::is_same_v<string,
    typename unpack<map<string, int>>::first
>);

static_assert(std::is_same_v<int,
    typename unpack<map<string, int>>::second
>);

如果您只关心在调用函数时这样做,您可以将函数设为模板:

template <typename K, typename V>
void foo(std::map<K, V>);

【讨论】:

  • 为了避免函数重载,unpack 可能应该为不是二进制模板实例化的类型返回一些默认实现,或者至少通过 bool 成员标志发出信号。
  • 实际上所有的麻烦都来自于我试图返回一个模板参数类型。如果我只是通过引用参数返回值,我会容易得多。但我仍然对仅使用基本模板语法在 C++ 中是否可以实现非专业化感到好奇。
  • @GwangmuLee 确实是这样,只是你需要定义一个 getter,就像 Vittorio 的 unpack 一样......而且你将无法涵盖所有可能的选项。
  • 所以你的意思是我们需要手动编写一个解包器。这有点令人失望。
【解决方案2】:

即兴表演:

template< class T >
struct Foo
{
    static auto impl() -> T;
};

template< class K, class V >
struct Foo< map< K, V > >
{
    static auto impl() -> map< K, V >;
};

template< class T >
auto foo()
    -> T
{ return Foo<T>::impl(); }

【讨论】:

    【解决方案3】:

    这是另一种可能的解决方案。方法 foo 分派到 foo_detail 方法,该方法将指向 T 的指针作为参数。该参数未在 foo_detail 中使用。相反,该参数允许重载决议选择调用哪个 foo_detail。

    由于未使用的参数,该解决方案给人一种笨拙的感觉。幸运的是,这可以隐藏在 SomeClass 的私有部分中,这样 SomeClass 的用户就不必知道它。

    #include <map>
    #include <iostream>
    #include <string>
    #include <typeinfo>
    using std::map;
    using std::cout;
    using std::endl;
    using std::string;
    
    class SomeClass
    {
    public:
        template <typename T>
        T foo()
            {
                return foo_detail((T *)0);
            }
    
    private:    
        template<typename T>
        T foo_detail(T *)
            {
                cout << "foo called with type " << typeid(T).name() << endl;
                return T();
            }
    
        template <typename K, typename V>
        map<K, V> foo_detail(map<K, V> *)
            {
                cout << "foo map specialization called with types "
                     << typeid(K).name() << ' ' << typeid(V).name() << endl;
                return map<K,V>();
            }
    };
    
    int main()
    {
        SomeClass s;
        s.foo<double>();
        s.foo<map<int, string> >();
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多