
AMDG On 05/24/2011 02:07 PM, Vicente Botet wrote:
Steven Watanabe-4 wrote:
On 05/23/2011 02:34 PM, Vicente Botet wrote:
I like how concepts are materialized using a template class that implements the default behavior of the concept and that can be specialized to map type to concepts. I understand that you need a second step to introduce the functions themselves specializing concept_interface.
What I don't understand is the overloading issues. Could you use a concrete example to try to clarify the problems you have addressed?
Okay. Problem 1: Name hiding for member functions.
typedef mpl::vector< callable<int(int)>, callable<double(double)>
test_concept;
If we just use the basic definition of concept_interface, the second overload will hide the first since the inheritance structure looks like:
struct callable1 { int operator()(int); };
struct callable2 : callable1 { double operator()(double); };
I find this normal and expected behavior, so I don't see yet why do you need to avoid this hiding.
It's normal in the sense that it's what C++ does by default. I don't see how it can possibly be the expected behavior that: struct func { template<class T> T operator()(T t) { return t; } }; typedef mpl::vector< callable<int(int)>, callable<double(double)>
test_concept;
func f1; any<test_concept> f(f1); f(1); // calls func::operator()<double>?? From the point of view of someone trying to use the library, the two instances of callable are equal. Having one hide the other is surprising.
I don't understand the use case of the example: when a using declaration will be needed?
I can't parse this sentence.
For free functions, I had a problem akin to name hiding when I defined a single namespace scope overload with concept_interface arguments. Using an inline friend function that takes the derived type works with some care to avoid duplicate definitions.
I'm sorry but I don't reach to see what is the real problem. I guess it is because I don't see the need of introducing the using sentence. Please could you present a real concrete case of overloading free functions that needs two different specializations of concept interface?
Consider operator+. Either the first argument or the second argument or both may be a type_erasure::any. I need two specializations, because there are two arguments. To use friend function defined inline I have to inject operator+ into exactly one of the arguments. I originally tried to handle free functions with namespace scope overloads like this: template<class Base, class T, class U, class R> typename rebind_any<Base, R>::type operator+( const concept_interface<addable<T, U, R>, Base, T>&, const U&); template<class Base, class T, class U, class R> typename rebind_any<Base, R>::type operator+( const T&, const concept_interface<addable<T, U, R>, Base, U>&); template<class Base1, class Base2, class T, class U, class R> typename rebind_any<Base, R>::type operator+( const concept_interface<addable<T, U, R>, Base1, T>&, const concept_interface<addable<T, U, R>, Base2, U>&); Unfortunately, this failed with the example print_sequence.cpp, because of ostreamable<_os, const char*>, ostreamable<_os, _t> Only one of these was being considered for overload resolution.
You can check the tests for details on what I expect to work. Most of the tests for specific concepts have a test_overload test case.
I've read some of them. I guess I understand what you expect to work, but I don't see why you need to specialize twice the concept_interface class for the same concept.
I couldn't get all the tests to pass with any other solution I tried. If you have a simpler way to specialize concept_interface for ostreamable or callable that passes all my tests I'd love to hear about it, but I've tried and this is the best I could come up with. To summarize the reasons for multiple specializations: - For member functions, the second and subsequent instances of a concept need to be treated differently from the first, because they need an extra using declaration (which would be ill-formed in the first occurrence). - For non-member functions, one specialization is needed for each argument that can be a type_erasure::any to make sure that the function gets added to the overload set regardless of what subset of the arguments is erased. In Christ, Steven Watanabe