【问题标题】:Ambiguous template function specialization compile error模棱两可的模板函数特化编译错误
【发布时间】:2012-06-20 19:12:51
【问题描述】:

我目前正在尝试创建一组转换函数,通过一次调用,可以(尝试)将 JavaScript 对象 (CefV8Value) 转换为其 C++ 对象,并支持指针。

下面是转换函数(最后是指针转换):

template<typename T>
T convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value) {};

// Explicit type conversion functions
#define V8VALUE_TO_CPP_CONVERSION(type) \
      template<> type \
      convert_v8value_to_cpp<type>(const CefRefPtr<CefV8Value> &value)

V8VALUE_TO_CPP_CONVERSION(CefRefPtr<CefV8Value>)
{
    return value;
}

V8VALUE_TO_CPP_CONVERSION(bool)
{
    return value->GetBoolValue();
}

V8VALUE_TO_CPP_CONVERSION(int)
{
    return value->GetIntValue();
}

V8VALUE_TO_CPP_CONVERSION(std::string)
{
    return value->GetStringValue().ToString();
}

V8VALUE_TO_CPP_CONVERSION(const char *)
{
    return value->GetStringValue().ToString().c_str();
}

V8VALUE_TO_CPP_CONVERSION(std::wstring)
{
    return value->GetStringValue().ToWString();
}

// HACKHACK: most VGUI functions take non-const wchar_t pointers, when they
// shouldn't
V8VALUE_TO_CPP_CONVERSION(wchar_t *)
{
    return (wchar_t*)value->GetStringValue().ToWString().c_str();
}

V8VALUE_TO_CPP_CONVERSION(const wchar_t *)
{
    return value->GetStringValue().ToWString().c_str();
}

V8VALUE_TO_CPP_CONVERSION(double)
{
    return value->GetDoubleValue();
}

V8VALUE_TO_CPP_CONVERSION(float)
{
    return value->GetDoubleValue();
}

//-----------------------------------------------------------------------------
// Purpose: converts a JS array to a C++ vector (of type T)
//-----------------------------------------------------------------------------
template<typename T>
std::vector<T> convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value)
{
    std::vector<T> vec;

    if(!value->IsArray())
        return vec;

    for(int i = 0; i < value->GetArrayLength(); ++i)
    {
        CefRefPtr<CefV8Value> element = value->GetValue(i);
        vec.push_back(convert_v8value_to_cpp<T>(element));
    }

    return vec; // hopefully move semantics will optimise this and not copy-construct
}

//-----------------------------------------------------------------------------
// Purpose: converts a JS object to a C++ pointer (where T is a pointer type)
//-----------------------------------------------------------------------------
template<typename T>
typename std::enable_if<std::is_pointer<T>::value, T>::type
    convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value)
{
    if(!value->IsObject())
        return NULL;

    CefRefPtr<CefV8Value> pTypeId = value->GetValue("__v8bind_typeid__");
    if(!pTypeId || !pTypeId->IsString())
        return NULL;

    CefRefPtr<CefV8Value> pPointerVal = value->GetValue("__v8bind_ptr__");
    if(!pPointerVal || !pPointerVal->IsInt())
        return NULL;

    if(pTypeId->GetStringValue().ToString() == typeid(T).name())
        return (T)pPointerVal->GetIntValue();

    return NULL;
}

下面是使用上述指针函数的代码:

WrapClass *pThis = convert_v8value_to_cpp<WrapClass*>(object);

Visual Studio 抱怨:

error C2668: 'convert_v8value_to_cpp' : ambiguous call to overloaded function
binding_test.cpp(106): could be 'Point *convert_v8value_to_cpp<WrapClass*>(const CefRefPtr<T> &)'
with
[
   WrapClass=Point,
   T=CefV8Value
]

binding_test.cpp(88): or       'std::vector<_Ty,_Ax> convert_v8value_to_cpp<WrapClass*>(const CefRefPtr<T> &)'
with
[
   _Ty=Point *,
   _Ax=std::allocator<Point *>,
   WrapClass=Point,
   T=CefV8Value
]

binding_test.cpp(31): or       'T convert_v8value_to_cpp<WrapClass*>(const CefRefPtr<CefV8Value> &)'
with
[
   T=Point *,
   WrapClass=Point
]

while trying to match the argument list '(CefRefPtr<T>)'
with
[
   T=CefV8Value
]

我不明白调用是如何模棱两可的(除了WrapClass * 匹配T 的第一个转换函数)。然而,它也表示可能的呼叫候选是std::vector 转换。这怎么可能?

提前非常感谢!

【问题讨论】:

  • 请看下面的答案。此外,您对 char *、wchar_t * 和 wchar_t const * 的转换返回指向临时的指针,这是未定义的行为。
  • 我没有注意到,我原本以为 To[W]String() 函数返回了引用...谢谢 Tomek!

标签: c++ templates template-specialization


【解决方案1】:

这两个:

template<typename T>
std::vector<T> convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value)
{ ... }

template<typename T>
typename std::enable_if<std::is_pointer<T>::value, T>::type
convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value)
{ ... }

不是函数的部分特化(无论如何都是不允许的),而是重载,因此与主函数模板一起,它们都是三个模棱两可的,因为它们仅在返回类型上有所不同。

您希望您的函数模板convert_v8value_to_cpp 委托给类模板convert_v8value_to_cpp_helper 中的静态函数(例如do_it()),因为与函数模板不同,类模板可以专门化。

初级类模板:

template <typename T>
struct convert_v8value_to_cpp_helper {}; // no do_it() here by intention

全部专业:

// Explicit type conversion functions
#define V8VALUE_TO_CPP_CONVERSION(type, code) \
template <> struct convert_v8value_to_cpp_helper< type > { \
  static T do_it() code \
}

V8VALUE_TO_CPP_CONVERSION(bool, {return value->GetBoolValue();});
V8VALUE_TO_CPP_CONVERSION(int,  {return value->GetIntValue(); });
V8VALUE_TO_CPP_CONVERSION(std::string,
{ return value->GetStringValue().ToString(); });
V8VALUE_TO_CPP_CONVERSION(const char *,
{ return value->GetStringValue().ToString().c_str(); });
V8VALUE_TO_CPP_CONVERSION(std::wstring,
{ return value->GetStringValue().ToWString(); });
// HACKHACK: most VGUI functions take non-const wchar_t pointers, when they
// shouldn't
V8VALUE_TO_CPP_CONVERSION(wchar_t *,
{ return (wchar_t*)value->GetStringValue().ToWString().c_str(); });
V8VALUE_TO_CPP_CONVERSION(const wchar_t *,
{ return value->GetStringValue().ToWString().c_str(); });
V8VALUE_TO_CPP_CONVERSION(double, {return value->GetDoubleValue();});
V8VALUE_TO_CPP_CONVERSION(float,  {return value->GetDoubleValue();});

std::vector 的专业化(包括具有自定义分配器的那些):

template<typename T, typename A>
struct convert_v8value_to_cpp< std::vector<T,A> > {
    static std::vector<T,A> do_it(const CefRefPtr<CefV8Value> &value)
    {
        std::vector<T,A> vec;

        if (!value->IsArray())
            return vec;

        for(int i = 0; i < value->GetArrayLength(); ++i)
        {
            CefRefPtr<CefV8Value> element = value->GetValue(i);
            vec.push_back(convert_v8value_to_cpp<T>(element));
        }

        return vec; // return value optimisation will kick in here
    }
};

最后是指针的特化:

template<typename T>
struct convert_v8value_to_cpp<T*> { // no need for enable_if
    static T* do_it(const CefRefPtr<CefV8Value> &value)
    {
        if (!value->IsObject())
            return nullptr; // don't use NULL in C++

        CefRefPtr<CefV8Value> pTypeId = value->GetValue("__v8bind_typeid__");
        if (!pTypeId || !pTypeId->IsString())
            return nullptr;

        CefRefPtr<CefV8Value> pPointerVal = value->GetValue("__v8bind_ptr__");
        if (!pPointerVal || !pPointerVal->IsInt())
            return nullptr;

        if (pTypeId->GetStringValue().ToString() == typeid(T).name())
            return (T*)pPointerVal->GetIntValue();

        return nullptr;
    }
};

这些现在用在真正的函数模板中:

template <typename T>
T convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value)
{ return convert_v8value_to_cpp_helper<T>::do_it(value); }

不再有歧义,因为只有一个函数模板(但如果部分模板专业化排序未能找到单个最佳匹配,则可能会出现歧义,但这里不应该是这种情况)。

【讨论】:

    【解决方案2】:

    mmutz 解释为什么它是模棱两可的(你有三个重载采用完全相同的参数类型,都可以匹配 convert_v8value_to_cpp&lt;WrapClass*&gt;

    编辑:我看到 mmutz 现在也添加了一个解决方案,类似于我的下面。我发现完整阅读它要容易得多,宏不会让我更容易阅读,甚至不会节省太多打字。

    如果你想部分特化一个模板,你需要使用一个类:

    template<typename T>
    struct Converter;
    
    template<>
    struct Converter<bool>
    {
      typedef bool result_type;
      static result_type convert(const CefRefPtr<CefV8Value> &value)
      { ... }
    };
    
    template<typename T>
    struct Converter<std::vector<T>>
    {
      typedef std::vector<T> result_type;
      static result_type convert(const CefRefPtr<CefV8Value> &value)
      { ... }
    };
    
    template<typename T>
    struct Converter<T*>
    {
      typedef T* result_type;
      static result_type convert(const CefRefPtr<CefV8Value> &value)
      { ... }
    };
    
    template<typename T>
    typename Converter<T>::result_type
    convert_v8value_to_cpp(const CefRefPtr<CefV8Value> &value)
    { return Converter<T>::convert(value); }
    

    【讨论】:

    • 谢谢乔纳森。这两个答案正是我想知道的,而且我在这个过程中学到了。然而,正如 Marc Mutz 首先回答的那样,我必须接受他的。
    • 他的回答还是比较完整的! :) 很高兴它帮助你理解
    猜你喜欢
    • 2011-07-21
    • 2021-07-26
    • 1970-01-01
    • 2011-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-12
    • 1970-01-01
    相关资源
    最近更新 更多