частичная специализация вариативное имя типа шаблона как пустое

У меня есть функция в библиотеке, которая является вариативным шаблоном и используется другой программой.

1

А.хпп

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

А.хпп

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

Теперь код в порядке, и моя библиотека отлично компилируется в .so/dll.

Но когда я использую f(...), компилятор находит только f2 из "A.tpl", а не f2 в .so/dll (из .cpp). Таким образом, код недействителен (опять же), потому что ret объявлен как недействительный.

Итак, если у кого-то есть какие-либо идеи, чтобы справиться с этим ...


Редактировать

Решение:

Сделайте 3 решение и добавьте A.tpl

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

person Krozark    schedule 05.05.2014    source источник
comment
Во-первых, в нем должно быть написано void A::f<void,Args ...>(int id,Args&& ... args), а не Ret A::f<void,Args ...>(int id,Args&& ... args).   -  person RamblingMad    schedule 05.05.2014
comment
Да, извините, это ошибка (копировать/вставить).   -  person Krozark    schedule 05.05.2014


Ответы (4)


Частичная специализация вспомогательного класса, вероятно, является самым элегантным способом, вы можете делать то, что написано в ответе Jarod42. Что касается того, почему ваше решение не сработало. Это потому, что вам нужно объявление специализации в вашем заголовочном файле, чтобы компилятор знал, что не следует создавать экземпляр с помощью универсального шаблона.

template <>
void A::f2<void>();
person kec    schedule 05.05.2014
comment
Частичная специализация функций не годится, так как вы не можете сделать это в C++. - person Yakk - Adam Nevraumont; 05.05.2014
comment
@Yakk: я сослался на ответ Jarod42, который является частичной специализацией класса. Но я отредактирую свой ответ, чтобы прояснить это. - person kec; 05.05.2014

Вы не можете частично специализировать функцию/метод...
Вы можете использовать вспомогательный класс, который может быть частично специализирован.

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)...);
    }
};
person Jarod42    schedule 05.05.2014

Я думаю, что 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);

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.

person RamblingMad    schedule 05.05.2014

Диспетчеризация тегов здесь проста и понятна

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
}
person Yakk - Adam Nevraumont    schedule 05.05.2014