【问题标题】:partial specialization variadic template typename as void部分特化可变参数模板类型名称为 void
【发布时间】:2014-05-05 12:13:56
【问题描述】:

我有一个函数,在一个库中,它是一个可变参数模板,并被其他程序使用。

1

A.hpp

class A {
    template<typename Ret,typename ... Args>
    static Ret f(int id,Args&& ... args);
};
#include "A.tpl"

A.tpl

template<typename Ret,typename ... Args>
Ret A::f(int id,Args&& ... args)
{
    //do somthing with args and id
    Ret ret;
    /// do somthing with ret
    return ret;
}

我的问题是这个: 如果 Ret 无效,则代码不正确。 所以我尝试建立一个 f 的专业化:

2

A.tpl

template<typename ... Args>
void A::f<void,Args ...>(int id,Args&& ... args)
{
    //do somthing with args and id
    return;
}

template<typename Ret,typename ... Args>
Ret A::f(int id,Args&& ... args)
{
    //do somthing with args and id
    Ret ret;
    /// do somthing with ret
    return ret;
}

但是这段代码不正确。


所以我尝试拆分代码:

3

A.hpp

class A {
    template<typename Ret,typename ... Args>
    static Ret f(int id,Args&& ... args);

   template<typname Ret>
   static Ret f2();
}
#include "A.tpl"

A.tpl

template<typename Ret,typename ... Args>
Ret A::f(int id,Args&& ... args)
{
    //do somthing with args and id
    return f2<Ret>();
}

template<typename Ret>
Ret A::f2()
{
    Ret ret;
    /// do somthing with ret
    return ret;
}

A.cpp

template<>
void A::f2<void>()
{
     return;
}

现在代码没问题,我的 lib 在 .so/dll 中编译得很好。

但是当我使用 f(...) 时,编译器只能找到“A.tpl”中的 f2,而不是 .so/dll(来自 .cpp)中的 f2。因此代码无效(再次),因为 ret 声明为 void。

所以,如果有人有任何想法来处理这个......


编辑

解决方案:

做3个解决方案,并添加A.tpl

template<>
void A::f2<void>();

【问题讨论】:

  • 首先,应该说void A::f&lt;void,Args ...&gt;(int id,Args&amp;&amp; ... args)而不是Ret A::f&lt;void,Args ...&gt;(int id,Args&amp;&amp; ... args)
  • 是的,对不起,这是一个错误(复制/过去)。

标签: c++ templates c++11 specialization variadic


【解决方案1】:

您不能对函数/方法进行部分特化...
你可以使用一个可以部分专门化的辅助类。

template<typename Ret, typename ... Args> struct helper;

template<typename ... Args>
struct helper<void, Args...>
{
    void operator() (int id, Args&& ... args) const { /* Implementation for void */ }
};

template<typename Ret, typename ... Args>
struct helper<Ret, Args...>
{
    Ret operator() (int id, Args&& ... args) const { /* Implementation for non void case */ }
};

class A {
    template<typename Ret,typename ... Args>
    static Ret f(int id, Args&& ... args)
    {
        return helper<Ret, Args...>()(id, std::forward<Args>(args)...);
    }
};

【讨论】:

    【解决方案2】:

    帮助类的部分专业化可能是最优雅的方法,您可以按照 Jarod42 的答案进行操作。至于为什么您的解决方案不起作用。这是因为您需要在头文件中声明特化,以便编译器知道不使用泛型模板进行实例化。

    template <>
    void A::f2<void>();
    

    【讨论】:

    • 函数的部分特化绝不是可行的方法,因为你不能在 C++ 中做到这一点。
    • @Yakk:我提到了 Jarod42 的答案,它是一个类的部分专业化。但我会编辑我的答案以明确这一点。
    【解决方案3】:

    我认为 SFINAE 可以解决您的问题,然后您可以在类型为 void 时禁用该功能,并在其为空时启用单独的功能。

    在您的返回类型中使用std::enable_if,如下所示:

    template<typename Ret, typename ... Args>
    typename std::enable_if<!std::is_void<Ret>::value, Ret>::type
    f(int id,Args&& ... args);
    

    这确实很难阅读,因此您可以像这样使其不那么冗长:

    template<bool Enable, class T = void>
    using enable_if_t = typename std::enable_if<Enable, T>::type
    
    template<typename Ret, typename ... Args>
    enable_if_t<!std::is_void<Ret>::value, Ret> f(int id, Args&& ... args);
    

    enable_if_t 如果您使用的是 C++14,将被预定义,顺便说一句。

    Ret = void 时你应该可以这样做:

    template<tpyename Ret, typename ... Args>
    enable_if_t<std::is_void<Ret>::value> f(int id, Args&& ... args);
    

    像这样在你的类中定义两者:

    class A{
        public:
            template<typename Ret, typename ... Args>
            static enable_if_t<!std::is_void<Ret>::value, Ret> f(int id, Args&& ... args);
    
            template<typename Ret, typename ... Args>
            static enable_if_t<std::is_void<Ret>::value> f(int id, Args&& ... args);
    };
    

    然后用相同的返回类型实现它们。

    你可以在这里看到它的实际效果:http://ideone.com/Wdmn5q

    【讨论】:

      【解决方案4】:

      这里的标签调度干净简单

      class A {
      private:
        template<typename Ret,typename ... Args>
        static void f_impl(std::true_type /* Ret_is_void */, int id,Args&& ... args);
        template<typename Ret,typename ... Args>
        static Ret f_impl(std::false_type /* Ret_is_void */, int id,Args&& ... args);
      public:
        // forward to one of the two _impl versions above.
        // based on if ret is void or not.  While this seems to return void_expression
        // such is valid in certain cases involving template code, including this one!
        template<typename Ret,typename ... Args>
        static Ret f(int id,Args&& ... args) {
          return f_impl<Ret>( std::is_same<Ret,void>{}, id, std::forward<Args>(args)... );
        }
      };
      
      template<typename Ret,typename ... Args>
      Ret A::f_impl(std::false_type /* Ret_is_void */ int id,Args&& ... args)
      {
        static_assert( !std::is_same<Ret,void>::value, "this version is only valid with Ret as non-void");
        //do somthing with args and id
        Ret ret;
        // do somthing with ret
        return ret;
      }
      template<typename Ret,typename ... Args>
      void A::f_impl(std::true_type /* Ret_is_void */ int id,Args&& ... args)
      {
        static_assert( std::is_same<Ret,void>::value, "this version is only valid with Ret as void");
        // whatever you do when Ret is non-void
      }
      

      【讨论】:

        猜你喜欢
        • 2016-06-23
        • 1970-01-01
        • 2012-01-14
        • 2017-08-13
        • 2012-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多