The following code compiles just fine under Visual Studio but neither gcc 4.6.2 or 4.7 can handle it. It seems to be valid but gcc can't seem to resolve the difference between const and non const parameters. Could this be a compiler bug?
struct CReadType{};
struct CWriteType{};
template<typename ReadWriteType, typename T>
struct AddPkgrConstByType {};
template<typename T>
struct AddPkgrConstByType<CReadType, T> {
typedef T type;
};
template<typename T>
struct AddPkgrConstByType<CReadType, const T> {
typedef T type;
};
template<typename T>
struct AddPkgrConstByType<CWriteType, T> {
typedef T const type;
};
template<typename Packager, typename T>
struct AddPkgrConst : public AddPkgrConstByType<typename Packager::CReadWriteType, T> {
};
template<typename Packager, typename T>
inline bool Package( Packager* ppkgr, T* pt )
{
return true;
}
template<typename Packager>
inline bool Package( Packager* ppkgr, typename AddPkgrConst<Packager,bool>::type* pb)
{
return false;
}
struct ReadPackager {
typedef CReadType CReadWriteType;
};
struct WritePackager {
typedef CWriteType CReadWriteType;
};
int main(int argc, char* argv[])
{
ReadPackager rp;
WritePackager wp;
bool b = true;
const bool cb = false;
Package( &rp, &b );
}
Compiler call:
g++ -fPIC -O -std=c++0x -Wno-deprecated -D_REENTRANT
g++-D__STDC_LIMIT_MACROS -c test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:58:22: error: call of overloaded ‘Package(ReadPackager*, bool*)’ is ambiguous
test.cpp:58:22: note: candidates are:
test.cpp:31:6: note: bool Package(Packager*, T*) [with Packager = ReadPackager, T = bool]
test.cpp:38:6: note: bool Package(Packager*, typename AddPkgrConst<Packager, bool>::type*) [with Packager = ReadPackager, typename AddPkgrConst<Packager, bool>::type = bool]
This looks like a compiler error to me. The issues involved here are overload resolution and partial ordering of template functions. Since both template functions can match the argument list (ReadPackager*, bool)
, partial ordering of template functions should be used to choose the more specialized template function.
Put simply, a template function is at least as specialized as another if the arguments to that function can always be used as arguments to the other.
It's clear that any two pointer arguments match the first Package() function, but for instance Package(ReadPackager*, const int*) can not match the second. This seems to imply that the second Package function is more specialized and ought to resolve any ambiguity.
However, since there is disagreement among the compilers, there may be some subtleties involved that are overlooked by the simplified explanation. I will therefore follow the procedure for determining function template partial ordering from the standard to discern the correct behavior.
First, labeling the functions as P1 and P2 for easy reference.
P1:
template<typename Packager, typename T>
bool Package( Packager* ppkgr, T* pt );
P2:
template<typename Packager>
bool Package( Packager* ppkgr, typename AddPkgrConst<Packager,bool>::type* pb);
The standard says that for each template function (T1), we must generic unique type for each of its template parameters, use those types to determine the function call parameter types, and then use those types to deduce the types in the other template (T2). If this succeeds, the first template (T1) is at least as specialized as the second (T2). First P2->P1
U
for template parameter Packager
of P2.P1
's parameter list. Packager
is deduced to be U
and T
is deduced to be AddPkgrConst<Packager,U>::type
.This succeeds and P1 is judged to be no more specialized than P2.
Now P1->P2:
U1
and U2
for template parameters Packager
and T
of P1 to get the parameter list (U1*, U2*).P2
's parameter list. Packager
is deduced to be U1.AddPkgrConst<U1,bool>::type
which evaluates to bool
. This does not match the second parameter U2
.This procedure fails if we proceed to step 4. However, my suspicion is that the compilers that reject this code don't perform step 4 and therefore consider P2 no more specialized than P1 merely because type deduction succeeded. This seems counter intuitive since P1 clearly accepts any input that P2 does and not vice versa. This part of the standard is somewhat convoluted, so it's not clear whether this final comparison is required to be made.
Let's try to address this question by applying §14.8.2.5, paragraph 1, Deducing template arguments from a type
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
In our type deduction, the deduced A is AddPkgrConst<U1,bool>::type
=bool
. This is not compatible with the original A, which is the unique type U2
. This seems to support the position that the partial ordering resolves the ambiguity.
I don't know what's wrong with Visual Studio, but what gcc
says seems right:
You instantiate AddPkgrConstByType<CReadType, T>
because Packager::CReadWriteType
resolves to CReadType
. Therefore, AddPkgrConst<Packager,bool>::type
will resolve according to the first implementation (which is not a specialisation) to bool
. This means you have two separate function specialisations with the same parameter list, which C++ doesn't allow you.
Since function templates can't be specialized, what you have here is two function template overloads. Both of these overloads are capable of accepting a bool*
as their second argument so they appear to properly be detected as ambiguous by g++.
However classes can be partially specialized so that only one version will be picked, and you can use wrapper magic to attain your desired goal. I'm only pasting the code I added.
template <typename Packager, typename T>
struct Wrapper
{
static bool Package()
{
return true;
}
};
template <typename Packager>
struct Wrapper<Packager, typename AddPkgrConst<Packager,bool>::type>
{
static bool Package()
{
return false;
}
};
template <typename Packager, typename T>
Wrapper<Packager, T> make_wrapper(Packager* /*p*/, T* /*t*/)
{
return Wrapper<Packager, T>();
}
int main()
{
ReadPackager rp;
bool b = true;
std::cout << make_wrapper(&rp, &b).Package() << std::endl; // Prints out 0.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With