«Неоднозначный вызов перегруженной функции» при использовании шаблонов с переменными значениями в VS2013

0

Учитывая следующий код:

        template <typename T>
        bool TryQueryInterface(
            IUnknown* in_toQuery,
            REFIID riid,
            void **ppvObject,
            ComObject* in_parent,
            HRESULT out_result)
        {
            if (InterfaceProperties<T>::GetIID() == riid)
            {
                void *underlying;
                HRESULT result = in_toQuery->QueryInterface(riid, &underlying);
                if (SUCCEEDED(result))
                {
                    *ppvObject = new typename InterfaceProperties<T>::WrapperClass(
                        *this,
                        (T*)underlying,
                        in_parent);
                }

                return true;
            }

            return false;
        }

        template <typename T, typename... Interfaces>
        bool TryQueryInterfaces(
            IUnknown* in_toQuery,
            REFIID riid,
            void **ppvObject,
            ComObject* in_parent,
            HRESULT out_result)
        {
            return TryQueryInterface<T>(in_toQuery, riid, ppvObject, in_parent, out_result) ||
                   TryQueryInterfaces<Interfaces...>(in_toQuery, riid, ppvObject, in_parent, out_result);
        }

        template <typename T>
        bool TryQueryInterfaces(
            IUnknown* in_toQuery,
            REFIID riid,
            void **ppvObject,
            ComObject* in_parent,
            HRESULT out_result)
        {
            return TryQueryInterface<T>(in_toQuery, riid, ppvObject, in_parent, out_result);
        }

Я получаю следующую ошибку:

    error C2668: 'TryQueryInterfaces' : ambiguous call to overloaded function
TryQueryInterfaces<ITrusteeGroupAdmin>(IUnknown *,const IID &,void **,ComObject *,HRESULT)'
 or       TryQueryInterfaces<ITrusteeGroupAdmin,>(IUnknown *,const IID &,void **,ComObject *,HRESULT)'
          while trying to match the argument list '(IUnknown *, const IID, void **, ComObject *, HRESULT)'
see reference to function template instantiation 'bool TryQueryInterfaces<ITrusteeAdmin,ITrusteeGroupAdmin>(IUnknown *,const IID &,void **,ComObject *,HRESULT)' being compiled

Что мне здесь не хватает? Как создать однозначный базовый регистр для рекурсии?

Теги:
c++11
templates
visual-studio-2013
variadic-templates

4 ответа

3
Лучший ответ

Это неоднозначно, потому что пакет параметров Interfaces... может быть пустым. Убедитесь, что вы взяли хотя бы один аргумент плюс ряд дополнительных (возможно, нулевых) параметров. Измените второй метод на:

template <typename T, typename Interface, typename... Interfaces>
bool TryQueryInterfaces(
    IUnknown* in_toQuery,
    REFIID riid,
    void **ppvObject,
    ComObject* in_parent,
    HRESULT out_result)
{
    return TryQueryInterface<T>(in_toQuery, riid, ppvObject, in_parent, out_result) ||
           TryQueryInterfaces<Interface, Interfaces...>(in_toQuery, riid, ppvObject, in_parent, out_result);
}
  • 0
    Это сработало, спасибо!
3

Функции перегрузки с вариативными шаблонами немного отличаются от обычных шаблонов.

Вы можете перегружать:

void foo()
{
}

а также

template <typename Arg1, typename ...Args>
void foo()
{
}

но нет

template <typename Arg1>
void foo()
{
}

а также

template <typename Arg1, typename ...Args>
void foo()
{
}

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

В вашем случае вы можете использовать вспомогательный класс для обработки вызовов.

// Forward declaration of the helper class and its template arguments.
template <typename ...Types> struct TryQueryInterfacesHelper;

// helper class with just one argument.
template <typename T>
struct TryQueryInterfacesHelper<T>
{
   bool operator()(IUnknown* in_toQuery,
                   REFIID riid,
                   void **ppvObject,
                   ComObject* in_parent,
                   HRESULT out_result)
   {
      return TryQueryInterface<T>(in_toQuery, riid, ppvObject, in_parent, out_result);
   }
};

// helper class with more than one argument.
template <typename T, typename ...Interfaces>
struct TryQueryInterfacesHelper<T, Interfaces...>
{
   bool operator()(IUnknown* in_toQuery,
                   REFIID riid,
                   void **ppvObject,
                   ComObject* in_parent,
                   HRESULT out_result)
   {
      return TryQueryInterface<T>(in_toQuery, riid, ppvObject, in_parent, out_result) ||
         TryQueryInterfacesHelper<Interfaces...>()(in_toQuery, riid, ppvObject, in_parent, out_result);
   }
};

template <typename... Interfaces>
bool TryQueryInterfaces(
      IUnknown* in_toQuery,
      REFIID riid,
      void **ppvObject,
      ComObject* in_parent,
      HRESULT out_result)
{
   // Implement the function using the helper class.
   return TryQueryInterfacesHelper<Interfaces...>()(in_toQuery, riid, ppvObject, in_parent, out_result);
}

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

#include <iostream>

template <class ...Types> struct X;

template <class T>
struct X<T>
{
   void operator()(){std::cout << "Came to X<T>\n";}
};

template <class T, class ...Types>
struct X<T, Types...> 
{
   void operator()(){std::cout << "Came to X<T, Types...>\n"; X<Types...>()();}
};

template <class ...Types>
void foo()
{
   X<Types...>()();
}

int main()
{
   foo<int>();
   foo<int, double>();
   foo<int, double, char>();
}

Вывод:

Came to X<T>
Came to X<T, Types...>
Came to X<T>
Came to X<T, Types...>
Came to X<T, Types...>
Came to X<T>
2

Другим решением было бы использовать enable_if чтобы включить реализацию TryQueryInterfaces, используя пакет параметров, когда пакет не пуст. Вы также должны изменить две реализации TryQueryInterfaces чтобы случай терминала, обрабатывающий один параметр шаблона, был выше другого. Это позволяет успешно выполнить поиск имени в версии пакета параметров.

Это также позволяет избавиться от реализации TryQueryInterface.

template<typename T>
bool TryQueryInterfaces(IUnknown* in_toQuery,
                        REFIID riid,
                        void **ppvObject,
                        ComObject* in_parent,
                        HRESULT out_result)
{
    // query single interface here
}

template<typename T, typename... Interfaces>
typename std::enable_if<sizeof...(Interfaces) != 0, bool>::type
 TryQueryInterfaces(IUnknown* in_toQuery,
                    REFIID riid,
                    void **ppvObject,
                    ComObject* in_parent,
                    HRESULT out_result)
{
    return TryQueryInterfaces<T>(in_toQuery, riid, ppvObject, in_parent, out_result) || 
           TryQueryInterfaces<Interfaces...>(in_toQuery, riid, ppvObject, in_parent, out_result);
}

Демо-версия

0
 template <typename T>
 void foo();

а также

 template <typename T, template ...R>
 void foo();

оба соответствуют foo<X>();

Чтобы сделать разницу, используйте два типа:

 template <typename T1, typename T2, template ...R>
 void foo();

Ещё вопросы

Сообщество Overcoder
Наверх
Меню