部分特化variadic模板typename为void

时间:2014-05-05 12:13:57

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

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

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中的f2(来自.cpp)。所以代码无效(再次),因为ret声明为void。

所以,如果有人有任何想法来解决这个问题......


修改

解决方案:

执行3解决方案,并添加A.tpl

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

4 个答案:

答案 0 :(得分:2)

你不能局部专门化一个功能/方法......
您可以使用可以部分专用的辅助类。

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)...);
    }
};

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

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

在您的返回类型中使用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);
如果您正在使用C ++ 14,那么

enable_if_t将被预定义。

您应该可以在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

答案 3 :(得分:0)

标签调度在这里简洁明了

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
}