0

I have a function,in a library, that is a variadic template, and is used by a other programme.

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

My problem is this one: If Ret is void, the code is not correct. So i try to build a specialization of 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;
}

But this code is not correct.


so I try to split the code:

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

Now the code is ok, and my lib compile fine in a .so/dll.

But when I use f(...), only f2 from "A.tpl" is find by the compiler, and not f2 in the .so/dll (from the .cpp). So the code is not valid (again), because of ret declared as void.

So, if anyone have any idea to deal with this ...


Edit

Solution:

Do the 3 solution, and add in A.tpl

template<>
void A::f2<void>();
Krozark
  • 862
  • 9
  • 27

4 Answers4

2

You can't partial specialize a function/method...
You may use a helper class which can be partial specialized.

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)...);
    }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

Partial specialization of a helper class is probably the most elegant the way to go, you can do what is in Jarod42's answer. As to why your solution didn't work. It's because you need a declaration of the specialization in your header file so that the compiler will know not to instantiate with the generic template.

template <>
void A::f2<void>();
kec
  • 2,099
  • 11
  • 17
0

I think SFINAE could fix your problem, then you could just disable the function for when the type is void and enable a separate function for when it is.

Use std::enable_if in your return type like this:

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

this is admittidly hard to read so you could make it less verbose like this:

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 will be pre-defined if you're using C++14, btw.

You should be able to do the same for when Ret = void:

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

Define both in you class like this:

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

Then implement them with the same return type.

You can see it in action here: http://ideone.com/Wdmn5q

RamblingMad
  • 5,332
  • 2
  • 24
  • 48
0

Tag dispatching is clean and simple here

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
}
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524