B
Bart Samwel
Hi everybody,
I would really like some help explaining this apparent discrepancy,
because I really don't get it. Here is the snippet:
void foo(int&);
void foo(int const&);
template<typename T>
void bar(T&);
int main()
{
foo(10); // calls foo(const int&)
bar(10); // calls bar<int>(int&)
}
The discrepancy is that apparently in the call to bar(), "10" is
interpreted as a const int, while in the call to bar(), "10" is
interpreted as a _non-const_ int. This behaviour is seen in g++ 3.3.5,
MSVC++ 7.1 and Comeau. To make things worse, the second call is then
always _rejected_ by the compiler, because (quoting MSVC++ 7.1):
"cannot convert parameter 1 from 'int' to 'int &': A reference that is
not to 'const' cannot be bound to a non-lvalue". So why is the "const
int" overload selected when no templates are involved, but the "int"
overload with the template?
For those interested, a bit of context on how I ran into this. In
MSVC++ 6.0 we had a "pass-through" construct for function arguments,
which boiled down to something like this:
template<typename T, typename T1, typename T2, typename T3>
void my_new(T1& a1, T2& a2, T3& a3)
{
/* ... */
new T(a1, a2, a3)
/* ... */
}
The reason we needed this was because we wanted to "wrap" the
constructed object into some other object (in our case, a container for
reference counting information) and only return a reference to that
object, but we still needed to pass constructor parameters. *Any* set
of constructor parameters, because it was a completely generic system.
So, we had a function template like this for any number of arguments
between 0 and 20, and it worked perfectly. If we would do something
like my_new<Foo>(10,20,30), then MS VC++ 6.0 would use my_new<Foo,const
int,const int,const int>. But now that we're trying this in newer, more
compliant compilers, it suddenly doesn't work, and we haven't found
another simple way of building a "generic argument passthrough" that
works. The only solution we've found is to add overloads for any
combination of const and non-const parameters:
template<typename T, typename T1>
void my_new(T1& a1)
{
}
template<typename T, typename T1>
void my_new(const T1& a1)
{
}
int main()
{
// This calls my_new<Foo,int>(const int&) -- even though the
// template argument is deduced as non-const int, the const
// overload is then selected. Again, WHY?
my_new<Foo>(10);
}
But the number of const/non-const combinations rises exponentially with
the number of arguments, so it's already not feasible to do this for
the versions with over five arguments.
So, we're basically out of a
good way to do generic passthrough. If anybody has any ideas on how we
could simulate a decent passthrough (that preserves reference
semantics, no copies allowed, and that selects the right overload when
passing the data on to another function!) I'd be much obliged...
Regards,
Bart Samwel
I would really like some help explaining this apparent discrepancy,
because I really don't get it. Here is the snippet:
void foo(int&);
void foo(int const&);
template<typename T>
void bar(T&);
int main()
{
foo(10); // calls foo(const int&)
bar(10); // calls bar<int>(int&)
}
The discrepancy is that apparently in the call to bar(), "10" is
interpreted as a const int, while in the call to bar(), "10" is
interpreted as a _non-const_ int. This behaviour is seen in g++ 3.3.5,
MSVC++ 7.1 and Comeau. To make things worse, the second call is then
always _rejected_ by the compiler, because (quoting MSVC++ 7.1):
"cannot convert parameter 1 from 'int' to 'int &': A reference that is
not to 'const' cannot be bound to a non-lvalue". So why is the "const
int" overload selected when no templates are involved, but the "int"
overload with the template?
For those interested, a bit of context on how I ran into this. In
MSVC++ 6.0 we had a "pass-through" construct for function arguments,
which boiled down to something like this:
template<typename T, typename T1, typename T2, typename T3>
void my_new(T1& a1, T2& a2, T3& a3)
{
/* ... */
new T(a1, a2, a3)
/* ... */
}
The reason we needed this was because we wanted to "wrap" the
constructed object into some other object (in our case, a container for
reference counting information) and only return a reference to that
object, but we still needed to pass constructor parameters. *Any* set
of constructor parameters, because it was a completely generic system.
So, we had a function template like this for any number of arguments
between 0 and 20, and it worked perfectly. If we would do something
like my_new<Foo>(10,20,30), then MS VC++ 6.0 would use my_new<Foo,const
int,const int,const int>. But now that we're trying this in newer, more
compliant compilers, it suddenly doesn't work, and we haven't found
another simple way of building a "generic argument passthrough" that
works. The only solution we've found is to add overloads for any
combination of const and non-const parameters:
template<typename T, typename T1>
void my_new(T1& a1)
{
}
template<typename T, typename T1>
void my_new(const T1& a1)
{
}
int main()
{
// This calls my_new<Foo,int>(const int&) -- even though the
// template argument is deduced as non-const int, the const
// overload is then selected. Again, WHY?
my_new<Foo>(10);
}
But the number of const/non-const combinations rises exponentially with
the number of arguments, so it's already not feasible to do this for
the versions with over five arguments.
good way to do generic passthrough. If anybody has any ideas on how we
could simulate a decent passthrough (that preserves reference
semantics, no copies allowed, and that selects the right overload when
passing the data on to another function!) I'd be much obliged...
Regards,
Bart Samwel