Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VS compiler error C2752 ("more than one partial specialization matches") in STL

Somehow I like these "shortest" programs showing a (fundamental?) problem. When testing some template code in VS2008 this error showed up (it has also been confirmed for VS2010 and VS2012, see below):

c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(225) : error C2752: 'std::_Ptr_cat_helper<_T1,_T2>' : more than one partial specialization matches the template argument list

   with
   [
       _T1=const float (**),
       _T2=const float (**)
   ]

I could boil down the problem to the following three lines:

#include <vector>
typedef float TPoint[3];
std::vector<TPoint const*> points; // error C2752

Note that the following is all ok

#include <vector>
#include <list>
typedef float TPoint[3];
// these similar usages of TPoint are all ok:
std::vector<TPoint*> points; // no error
TPoint const* points1[2];
std::list<TPoint const*> points2;

I tried to fix xutility by supplying additional template spezializations for struct _Ptr_cat_helper - without luck. Any ideas what goes wrong? Or how to work around without loosing the const?

like image 814
coproc Avatar asked Oct 06 '12 18:10

coproc


1 Answers

The problem is indeed an ambiguity in a partial specialization:

Internally, the allocator uses some metaprogramming (_Ptr_cat) to determine if the dtor is to be called on the elements of the vector (or do nothing). This metaprogramming tries to differentiate some cases by using partial specialization. _Ptr_cat uses _Ptr_cat_helper which is being specialized on the vector's allocator's pointer type -- the vector's value_type is TPointer const* == const float (*)[3], so the allocator of the vector has a pointer type const float(**)[3].

When you use std::vector < const float(*)[3] >, the error message contains the [3] part, whereas using std::vector < TPoint const* >, the [3] is not displayed o.O

_Ptr_cat expects two template arguments of the same type with possibly different c-qualifier, e.g. float*, float const*. Now, the two input types are both const float (**)[3]. MSVC resolves them ambiguously to the specializations of _Ptr_cat_helper: Ty**, Ty const** and/or Ty**, Ty**. I verified that by writing a small example which mimics the partial specialization of _Ptr_cat_helper (just plain templates, no Std Lib involved).

Yet, I cannot explain why this happens. Strangely, there's no ambiguity when setting up an example using only one specialization parameter -- const float(**)[3] is resolved to Ty const** with Ty = float const[3] as expected.

Thanks to Peter Alexander, I also tried my simple example (2 template parameters) with g++, and it worked as expected, no ambiguity. Maybe this might be a compiler issue?

Let me propose some workarounds:

  • If you really want to modify the MSVC standard lib, you could add a specialization _Ptr_cat_helper < const Ty (**)[3], const Ty (**)[3] > which fits exactly and resolves the ambiguity. Unfortunately, you have to provide the dimension explicitly (3).
  • Do not use arrays here. Use std::array (available in tr1 in VS08) or structures or plain pointers (float const* instead of float const[3])
  • Use a simple wrapper:

    template < typename T > struct wrapper { T wrapped; }
    std::vector < wrapper < TPoint > const* > m;
    

    works fine for me.

Edit: here's the example I used:

#include <typeinfo>
#include <iostream>

template < typename T1,  typename T2 >
struct Spec
{
    static const char* check() { return "plain"; }
    typedef void Value;
};

#define MAKE_SPEC(ARG0, ARG1) \
template < typename T > \
struct Spec < ARG0, ARG1 > \
{ \
    static const char* check() { return #ARG0 ", " #ARG1; } \
    typedef T Value; \
}

MAKE_SPEC(T**, T**);
MAKE_SPEC(T**, T const**);
// can do more, but need not to..

int main()
{
    typedef Spec < const float(**)[3], const float(**)[3] > MySpec;

    std::cout << MySpec::check() << " -- " << typeid(MySpec :: Value).name();
}
like image 156
dyp Avatar answered Sep 21 '22 02:09

dyp