I have a code like this:
set<array<int, 3>> s;
int ar[3] = {1,2,3};
s.insert(ar);
auto it = s.find(ar);
And the message of IDE that no instance of overloaded function insert/find. If I use std::array it is ok, ::insert and ::find work in the same way.
But what if i wanna use these C arrays T[N] or find function with std::array in set should return iterator if two of three elements equal. Or set::insert will not insert a new array if it is a permutation of exist, I mean s.insert({1,2,3}) - already added, s.insert({3,2,1}) - return iterator on {1,2,3}.
The question is how to overload STD functions? Not in this code especially but in general? Any link or real code example welcome. May be its a really simple question for experienced programmers) but there are a lot of examples of operator overloading, but no for STL. Thanks.
You cannot declare additional overloads for a member function of a class outside the class definition. The language does not provide syntax for it. Since you can't modify the class definition in the standard library headers, you cannot add additional overloads to them either.
Free functions in the std namespace may not be overloaded. There is syntax for it (the same way any overloads of free functions work), but doing so causes undefined behavior, because it is explicitly forbidden in the standard, see [namespace.std]/1.
What is usually (exceptions apply) allowed is to define template specializations for entities in the std namespace, but only if the specialization depends on a user-defined type and not for member functions, member function templates or member class templates. See the rest of the quoted and following paragraph linked above.
A common example where specialization inside the std namespace is used is std::hash for use as hash function in unordered associative containers:
struct my_type {
//...
};
namespace std {
template<>
struct hash<my_type> {
auto operator()(my_type const& x) const noexcept {
//...
}
};
}
But even something like that is not allowed if my_type is replaced by int*, int[3] or std::array<int, 3> or anything similar, because there is no dependence on a user-declared type.
If you want to make a std::array from a built-in array, you will be able to use std::to_array in C++20:
set<array<int, 3>> s;
int ar[3] = {1,2,3};
s.insert(std::to_array(ar));
auto it = s.find(std::to_array(ar));
Before C++20, you might have the function available as std::experimental::to_array in #include<experimental/array> or you can define it yourself (from cppreference.com; requires #include<type_traits>, #include<utility> and #include<cstddef>):
namespace detail {
template <class T, std::size_t N, std::size_t... I>
constexpr std::array<std::remove_cv_t<T>, N>
to_array_impl(T (&a)[N], std::index_sequence<I...>)
{
return { {a[I]...} };
}
}
template <class T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N])
{
return detail::to_array_impl(a, std::make_index_sequence<N>{});
}
About the other question - about permutations, i.e., that you want your set to find iterator to {1,2,3} when you look for a permutation of it.
Technically, you can do 2 things:
(1) make a specialized comparison operator for which permutations are equivalent. Or make a class that wraps std::array and implement its comparison operators this way.
(2) (preferred) simply sort your array before adding it to set and/or searching for it.
If you need its elements in original order, then I recomend to make utility class that contains both the original array and sorted version that it uses for comparisons.
If you also want to be able to add option for having multiple original while returning the exact one when it exists... then its more complicated. It can be done with some composite functions.
Just like #walnut said, you cannot declare additional overloads for a member function of a class outside the class definition. Other than encapsulate your own int<3> class, there is another work around - operator << override.
set<array<int, 3>>& operator << (set<array<int, 3>>& s, int ar[3])
{
array<int,3> ar2 { ar[0], ar[1], ar[2] };
s.insert(ar2);
return s;
}
It looks weird, but easy to use.
Now you can do this
set<array<int, 3>> s;
int ar[3] = {1,2,3};
s << ar;
However, if there is a bunch of functions you want to use in set against int[3] against array, It is better to define your own class / struct.
template <class T, int arsize>
struct MyFixedArray
{
T ar[arsize];
MyFixedArray(T _ar[arsize]) {
for (int i = 0; i < arsize; ++i)
ar[i] = _ar[i];
};
operator array<T, arsize>() const {
array<T, arsize> _ar;
for (int i = 0; i < arsize; ++i)
_ar[i] = ar[i];
return _ar;
};
};
and now you can use it freely everwhere
MyFixedArray<int, 3> myar(ar);
s.insert(myar);
auto it = s.find(myar);
I don't think that C++ gives you the right to do so. For more information, you may get this answer useful in your case.
I hope that helps your problem.
Related
I'd like to swap 2 values, pointed by iterators. So I want to specialize template function for taking iterators. Could you tell, please, how could I do this?
Function for non-iterators:
template <typename T>
void exch(T &a, T &b)
{
T c( std::move(a) );
a = std::move(b);
b = std::move(c);
}
Function for iterators:
template <typename It>
void exch(It &a, It &b)
{
auto c( std::move(*a) );
*a = std::move(*b);
*b = std::move(c);
}
Of course, with 2 functions I get an error:
/projects/DFSTL/main.cpp:17:6: error: redefinition of ‘template void exch(It&, It&)’
I would advise against doing this -- swapping a and b, and swapping the things indirectly referred to by a and b, is a tempting thing to overload. But you end up getting really strange behavior if you do that.
If you must, the first step would involve writing your own is_iterator traits class. This can be done through various methods. One that works on some compilers is to use SFINAE resolution of std::iterator_traits<It>::iterator_category, and check if input_iterator_tag or output_iterator_tag is a base of that type.
You do usually need to detect void* and cv variants. This is still implementation defined behavior, as std::iterator_traits on a non-iterator generates implementation defined results.
The more complex way is to examine each of the type axioms of iterators and test them, and if all pass say it is an iterator.
Once you have is_iterator<T>, you can use SFINAE:
template <class T, class=std::enable_if_t< !is_iterator<std::decay_t<T>>{} >>
void exch(T &a, T &b) {
using std::swap;
swap(a,b);
}
Function for iterators:
template <class It, class=std::enable_if_t< is_iterator<std::decay_t<It>>{} >>
void exch(It a, It b) {
using std::iter_swap;
iter_swap(a,b);
}
note that I called swap and iter_swap in an ADL-enabled context with std:: lookup rather than manually implementing it. This allows types that specialize their own swap in their enclosing namespace to automatically get their more optimal solution.
Both those template functions look identical to the compiler. That's why you get the compiler error.
Have you tried using this function: std::swap()?
It looks like it does what you want - and has been specialized for all STL container types for which a swap makes sense.
The size of std::array is known at compile time, but the size member function isn't static. Is there any reason for that? It's slightly inconvenient not to be able to calculate the size without instantiating an object. (Well, I know about std::tuple_size specialization, but it doesn't work for classes derived from std::array.)
There is no good reason for that. In fact, boost::array<T, N>, the precursor of std::array<T,N>, actually defines static size_t size(){return N;} (although a modern more useful version should use constexpr also).
I agree with the OP that this is an unfortunate omission and underexplotaition of the language features.
Problem
I faced this problem before and the logic leads to a couple of solutions. The OP situation is the following: you have a class that derives from std::array and you need to access to the size at compile time.
#include<array>
template<class T...>
struct myarray : std::array< something that depends on T... >{
... very cool functions...
};
and later you have
template<class Array, size_t N = ???>
functionOnArrayConcept(Array const& a){...}
Where you need to know N at compile time.
As it is now, there is no code ??? that you can write that works both for std::array and myarray, because std::tuple_size<myarray<...>> will not work.
Solution
(this was suggested by #T.C. here Access maximum template depth at compile? . I am just copying it here.)
template<class T, std::size_t N>
auto array_size_impl(const std::array<T, N>&)
-> std::integral_constant<std::size_t, N>;
template<class Array>
using array_size = decltype(array_size_impl(std::declval<const Array&>()));
template<class Array>
constexpr auto static_size() -> decltype(array_size<Array>::value){
return array_size<Array>::value;
}
template<class Array>
constexpr auto static_size(Array const&) -> decltype(static_size<Array>()){
return static_size<Array>();
}
Now you can use it as this:
template<class Array, size_t N = static_size<Array>()>
functionOnArrayConcept(Array const& a){...}
If you are using std::tuple_size already, unfortunately (I think) you need to specialize std::tuple_size for each of your derived classes:
namespace std{
template<class... T> // can be more complicated if myarray is not parametrized by classes only
struct tuple_size<myclass<T...>> : integral_constant<size_t, static_size<myclas<T...>>()>{};
}
(In my opinion this is caused by another mistake in the STL design that std::tuple_size<A> doesn't have the default template<class A> struct tuple_size : A::size(){}.)
The solutions beyond this point are near obsolete compared to #T.C.
solution described above. I'll keep them here for reference only.
Solution 1 (idiomatic)
If the function is decoupled from you class you have to use std::tuple_size because that is the only standard way of accessing the size of std::array at compile time. Therefore you have to do this, 1) provide a specialization of std::tuple_size and if you can control myclass, 2) std::array doesn't have static size() but your derived class could (that simplifies the solution).
So, this can be a pretty general solution within the framework of STD, that consists in the specialization of std::tuple_size.
(Unfortunately providing specialization in std:: sometimes is the only way to make real generic code. See http://en.cppreference.com/w/cpp/language/extending_std)
template<class... T>
struct myarray : std::array<...something that depends on T...>{
... very cool functions...
static constexpr size_t size(){return std::tuple_size<std::array<...something that depends on T...>>::value;}
};
namespace std{
// specialization of std::tuple_size for something else that `std::array<...>`.
template<class... T> // can be more complicated if myarray is not parametrized by classes only
struct tuple_size<myclass<T...>> : integral_constant<size_t, myclass<T...>::size()>{};
}
// now `functionOnArrayConcept` works also for `myarray`.
(static size_t size() can be called differently, and there may be other ways to deduce the size of the base of myarray without adding any static function to size.)
Note
In the compilers I tried the following trick doesn't work. If this worked, the whole discussion would be less important, because std::tuple_size wouldn't be so necessary.
template<class ArrayConcept, size_t N = ArrayConcept{}.size()> // error "illegal expression", `std::declval<ArrayConcept>()` doesn't work either.
functionOnArrayConcept(ArrayConcept const& a){...}
Conceptualization
Due to this shortcoming in the implementation (or specification?) of std::array by which the only way to extract the compile time size is through std::tuple_size. Then std::tuple_size is conceptually part of the necessary interface of std::array. Therefore when you inherit from std::array you have also "inherit" std::tuple_size in some sense. And unfortunately you need to do this for further derivations. This is the concept behind this answer.
Solution 2 (a GNU hack)
If you are using GNU's STD library (that includes gcc and clang), there is a hack that can be used without adding any code, and that is by using the _M_elems member which is of (member) type ::_AT_Type::_Type (a.k.a. type T[N]) of std::array<T, N>.
This function will effectively behave like a static function ::size() (except that it cannot be used for instances of an object) of std::array or any type derived from std::array.
std::extent<typename ArrayType::_AT_Type::_Type>::value
which can be wrapped into:
template<class ArrayType>
constexpr size_t array_size(){
return std::extent<typename ArrayType::_AT_Type::_Type>::value
}
This work because the member type _AT_Type::_Type is inherited. (I wonder why GNU left this implementation detail public. Another omission?)
Solution 3 (a portable hack)
Finally, a solution using template recursion one can figure out what is the dimension of the base std::array.
template<class Array, size_t N=0, bool B = std::is_base_of<std::array<typename Array::value_type, N>, Array>::value>
struct size_of : size_of<Array, N + 1, std::is_base_of<std::array<typename Array::value_type, N+1>, Array>::value>{};
template<class Array, size_t N>
struct size_of<Array, N, true> : std::integral_constant<size_t, N>{};
// this is a replacement for `static Array::size()`
template<class Array, size_t N = size_of<Array>::value>
constexpr size_t static_size(){return N;}
// this version can be called with an object like `static Array::size()` could
template<class Array, size_t N = size_of<Array>::value>
constexpr size_t static_size(Array const&){return N;}
This is how one will get:
struct derived : std::array<double, 3>{};
static_assert( static_size<std::array<double, 3>>() == 3 );
static_assert( static_size<derived>() == 3 );
constexpr derived d;
static_assert( static_size(d) == 3 );
If this function is called with some type unrelated to std::array, it will give a recursion error. If you want a "soft" error instead, you have to add the specialization.
template<class Array>
struct size_of<Array, 250, false> {};
where 250 stands for a large number but smaller than the recursion limit. (I don't know how to get this number automatically, I only know the the recursion limit in my compiler is 256.)
Since C++11 you can use std::tuple_size on std::array to obtain the size as a compile time constant. See
http://en.cppreference.com/w/cpp/container/array/tuple_size
It can indeed be static, however, this would break "container" interface which won't play well with other generic algorithms that do expect containers to have size() member function. There is nothing to worry about, though, as std::array::size() is a constexpr function, so there is absolutely no overhead associated with it.
UPDATE:
Mr. Jrok have pointed out that one can call static member functions with "normal" syntax. Below is an example when it won't:
#include <array>
struct array {
static unsigned int size()
{
return 0;
}
};
template <typename T>
static auto do_stuff(T& data) -> decltype(data.size())
{
typedef decltype(data.size()) size_type;
size_type (T::*size_calc)() const = &T::size;
size_type n = 0;
for (size_type i = 0, e = (data.*size_calc)(); i < e; ++i)
++n;
return n;
}
int main()
{
// Below is fine:
std::array<int, 5> data { 1, 2, 3, 4, 5 };
do_stuff(data);
// This, however, won't work as "size()" is not a member function.
#if 0
array fake;
do_stuff(fake);
#endif
}
array::size is constexpr, so unless the stored type has a constructor or destructor, the operation array_t().size() is very unlikely to have any runtime effect. You can embed it in a template argument to ensure it doesn't. It does otherwise look like runtime code, though.
I think that it's nonstatic simply for uniformity with other containers. For example, you can form a pointer-to-member-function to it. Discovering the true rationale of anything often takes tough research, though. It could be that the authors never thought of it.
The other thing that comes to mind is that some special functions such as operator () () cannot be static, so any opportunistic application of static can only be piecemeal. Generic problems are better solved in uniform fashion, even if it means changing the core language.
You can re-declare a same-typed empty std::array (which should get optimized out) and take the size of that. For example:
// Pretend this is an expensive initialization; e.g., a function return value.
std::array<char, 0x123> some_array{1,2,3};
using type_of_some_array = decltype(some_array);
// Find the size without accessing the array.
auto constexpr size_of_some_array = type_of_some_array().size();
std::cout << size_of_some_array << std::endl;
compiles to:
00000000004006f0 <main>:
4006f0: 48 83 ec 08 sub $0x8,%rsp
4006f4: be 23 01 00 00 mov $0x123,%esi
4006f9: bf 60 10 60 00 mov $0x601060,%edi
4006fe: e8 ad ff ff ff callq 4006b0 <_ZNSo9_M_insertImEERSoT_#plt>
...
[No issues found when using size_of_some_array as a template parameter: which was counter-alluded to in the comments on #Potatoswatter's answer.]
Note that the Microsoft Visual C++ doesn't currently support constexpr (http://msdn.microsoft.com/en-us/library/hh567368.aspx), so the following valid code won't work:
array<int,3> dog;
array<double, dog.size( )> cat;
The following class provides a compile time static variable:
/**
* hack around MSVC's 2012 lack of size for const expr
*/
template <typename T, int N>
struct vcarray : public std::array<T,N> {
static const size_t ArraySize= N;
};
which can be used as:
vcarray<double,3> cat;
vcarray<double,cat.ArraySize> dog;
In my opinion it does not make sense to make the size member function static insofar as it provides no added value. It is possible to make it static, but you gain nothing from it.
The way the array class is designed, you can query the size of a given array object without explicitly knowing/remembering its exact type (which includes its size) at that location where you need the size. This is a convenience, and it removes the opportunity to make copy/edit errors. You can write code like this:
std::array<int, 5> blah;
// 50 lines of code
do_something_with(blah.size()); // blah knows its size
As you can see, at the location where I'm consuming the array's size, I don't actually remember what it was, but my code will work anyway, regardless of what the value actually is, and regardless whether maybe one day I change the array's type to be a different size.
Since the size function merely returns a template parameter, the compiler can trivially prove that the return value is a compile-time constant and optimize accordingly too (the function is also constexpr, so you can also use the return value as template parameter or enumeration).
Now what will be different if we make the size member function static?
If size was a static function, you could still use the static member function in the exact same way (that is, on an object instance, in a "not static way"), but that would be "cheating". After all, this is something that already works anyway, whether the member is static or not.
Further, you now have the possibility of invoking the member function without an object instance. While this seems like a good thing at first glance it really is no advantage at all for the array class template (...where the returned size is a template parameter).
In order to call a member function without an object (that is, in a "static member function way"), you must properly qualify the function with the class name and its proper template parameters.
In other words, you must write something like:
std::array<int, 5> blah;
// 50 lines of code
do_something_with(std::array<int,5>::size()); // I must tell size what to return
Now what have we gained from calling the size function? Nothing at all. In order to call the function, we needed to provide the correct template parameters, which includes the size.
That means no more and no less than that we must supply the information that we wish to query. Calling the function doesn't tell us anything we didn't already know.
I am practicing C++ by building my own version of vector, named "Vector". I have two constructors among others, fill constructor and range constructor. Their declarations look like following:
template <typename Type>
class Vector {
public:
// fill constructor
Vector(size_t num, const Type& cont);
// range constructor
template<typename InputIterator> Vector(InputIterator first, InputIterator last);
/*
other members
......
*/
}
The fill constructor fills the container with num of val; and the range constructor copies every value in the range [first, last) into the container. They are supposed to be the same with the two constructors of the STL vector.
Their definitions goes following:
//fill constructor
template <typename Type>
Vector<Type>::Vector(size_t num, const Type& cont){
content = new Type[num];
for (int i = 0; i < num; i ++)
content[i] = cont;
contentSize = contentCapacity = num;
}
// range constructor
template <typename Type>
template<typename InputIterator>
Vector<Type>::Vector(InputIterator first, InputIterator last){
this->content = new Type[last - first];
int i = 0;
for (InputIterator iitr = first; iitr != last; iitr ++, i ++)
*(content + i) = *iitr;
this->contentSize = this->contentCapacity = i;
}
However, when I try to use them, I have problem distinguishing them.
For example:
Vector<int> v1(3, 5);
With the this line of code, I intended to create a Vector that contains three elements, each of which is 5. But the compiler goes for the range constructor, treating both "3" and "5" as instances of the "InputIterator", which, with no surprises, causes error.
Of course, if I change the code to:
Vector<int> v1(size_t(3), 5);
Everything is fine, the fill constructor is called. But that is obviously not intuitive and user friendly.
So, is there a way that I can use the fill constructor intuitively?
You can use std::enable_if (or boost::enable_if if you don't use C++11) to disambiguate the constructors.
#include <iostream>
#include <type_traits>
#include <vector>
using namespace std;
template <typename Type>
class Vector {
public:
// fill constructor
Vector(size_t num, const Type& cont)
{
cout << "Fill constructor" << endl;
}
// range constructor
template<typename InputIterator> Vector(InputIterator first, InputIterator last,
typename std::enable_if<!std::is_integral<InputIterator>::value>::type* = 0)
{
cout << "Range constructor" << endl;
}
};
int main()
{
Vector<int> v1(3, 5);
std::vector<int> v2(3, 5);
Vector<int> v3(v2.begin(), v2.end());
}
The above program should first call the fill constructor by checking if the type is an integral type (and thus not an iterator.)
By the way, in your implementation of the range constructor, you should use std::distance(first, last) rather than last - first. Explicitly using the - operator on iterators limits you to RandomAccessIterator types, but you want to support InputIterator which is the most generic type of Iterator.
Even std::vector seems to have this issue.
std::vector<int> v2(2,3);
chooses
template<class _Iter>
vector(_Iter _First, _Iter _Last)
In Visual C++, even though it should match closer to the non templated case..
Edit: That above function (correctly) delegates the construction to the below one. I am totally lost..
template<class _Iter>
void _Construct(_Iter _Count, _Iter _Val, _Int_iterator_tag)
Edit #2 AH!:
Somehow this below function identifies which version of the constructor is meant to be called.
template<class _Iter> inline
typename iterator_traits<_Iter>::iterator_category
_Iter_cat(const _Iter&)
{ // return category from iterator argument
typename iterator_traits<_Iter>::iterator_category _Cat;
return (_Cat);
}
The above shown _Construct function has (atleast) 2 versions overloading on the third variable which is a tag to returned by the above _Iter_cat function. Based on the type of this category the correct overload of the _Construct is picked.
Final edit:
iterator_traits<_Iter> is a class that seems to be templated for many different common varieties, each returning the appropriate "Category" type
Solution: It appears template specialization of the first arguement's type is how the std library handles this messy situation (primitive value type) in the case of MS VC++. Perhaps you could look into it and follow suit?
The problem arises (I think) because with primitive value types, the Type and size_t variables are similar, and so the template version with two identical types gets picked.
The problem is the same at the one faced by the standard library implementation. There are several ways to solve it.
You can meticulously provide non-template overloaded constructors for all integral types (in place of the first parameter).
You can use SFINAE-based technique (like enable_if) to make sure the range constructor is not selected for integer argument.
You can branch the range constructor at run-time (by using if) after detecting integral argument (by using is_integral) and redirect control to the proper constructing code. The branching condition will be a compile-time value, meaning that the code will probably be reduced at compile-time by the compiler.
You can simply peek into your version of standard library implementation and see how they do it (although their approach is not required to be portable and/or valid from the point of view of abstract C++ language).
This ambiguity caused problems for early library implementers. It's called the "Do The Right Thing" effect. As far as I know, you need SFINAE to solve it… it might have been one of the first applications of that technique. (Some compilers cheated and hacked their overload resolution internals, until the solution was found within the core language.)
The standard specification of this issue is one of the key differences between C++98 and C++03. From C++11, §23.2.3:
14 For every sequence container defined in this Clause and in Clause 21:
— If the constructor
template <class InputIterator>
X(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type())
is called with a type InputIterator that does not qualify as an input iterator, then the constructor shall not participate in overload resolution.
15 The extent to which an implementation determines that a type cannot be an input iterator is unspecified, except that as a minimum integral types shall not qualify as input iterators.
While implementing a template class whose constructor takes:
vector of functions (functions are of type std::function<bool(const T&)>)
std::binary_function<bool,bool,bool> that I will use to accumulate results of applications of vector from 1) to certain value.
I want to be able to use std::plus() and std::multiplies() as second template parameter, but the problem is that depending on the function I need a corresponding neutral element (for std accumulate init value). For AND (std::multiplies) I need true (aka 1), for OR (std::plus) I need false (aka 0). I know I could just specialize the template and problem solved but I wonder if there is a way to get neutral element for a built in STL function.
If you're using gcc, you can use __gnu_cxx::identity_element, which is doing exactly what you're asking for.
If you're not, I don't think there is a general solution, as if there was, gcc wouldn't have implemented their own - you might just rewrite their implementation (which is actually just a couple of template specializations, as you expected).
Edit: the source code for this is on lines 78-98 of this file.
The usual solution here is traits. Rather than instantiate your
template on std::plus or whatever, you would instantiate it on a
traits class, which defines a typedef for std::plus, plus an
identity element (static const, with initializer), and anything else you
need. Something like:
struct OpTraitsAdd
{
typedef std::plus<int> Op;
static int const identity = 0;
};
struct OpTraitsMult
{
typedef std::multiplies<int> Op;
static int const identity = 1;
};
It's also possible to obtain the traits from the standard operator,
using explicit specialization:
template <typename Op> struct OpTraits;
template<>
struct OpTraits<std::plus<int> >
{
static int const identity = 0;
};
template<>
struct OpTraits<std::multiplies<int> >
{
static int const identity = 1;
};
In this case, you would instantiate your class over the operator, and
use OpTraits<Op>::identity when needed.
In both cases, of course, you do have to provide all of the necessary
traits, either as independent classes or as template specializations.
If the only two identity elements you need are 0 and 1, you might be
able to do it automatically with something like:
template <bool idIs0> struct IdImpl;
template<>
struct IdImpl<false>
{
static int value = 1;
};
template<>
struct IdImpl<true>
{
static int value = 0;
};
template <typename Op>
struct Id
{
static int value = ItImpl<Op(1, 0) == 1>::value;
};
This will not work pre-C++11, since Op(1, 0) is not a constant
epxression. I'm not sure about C++11; but I think that if
Op::operator() is declared constexpr, it should work. (I'd only
bother trying if I had to cover a lot of operators, including some that
clients might provide.)
Just to pick out one point from James's answer (and my comment to it). I think it deserves to be considered separately.
If you want to you can compute the identity at runtime, assuming that there really is an identity. It's !func(true, false).
If func::operator() is available and free of side-effects, as it is for std::plus and std::multiplies, then presumably any sensible compiler will actually compute that at compile time. But it doesn't need the value at compile time, so now your template could (if the caller wants) accept a std::function<bool(bool,bool)> instead of needing to know the actual accumulate operation at compile-time.
According to the standard, a conversion function has a function-id operator conversion-type-id, which would look like, say, operator char(&)[4] I believe. But I cannot figure out where to put the function parameter list. gcc does not accept either of operator char(&())[4] or operator char(&)[4]() or anything I can think of.
Now, gcc seems to accept (&operator char ())[4] but clang does not, and I am inclined to not either, since it does not seem to fit the grammar as I understand it.
I do not want to use a typedef because I want to avoid polluting the namespace with it.
You can use identity
template<typename T>
struct identity { typedef T type; };
struct sample {
operator identity<char[4]>::type &() {
...
}
};
You are correct that function and array declarators won't work in conversion functions. This is also known and discussed in this issue report. However i think that C++0x already provides a solution to what they discuss there
struct sample {
template<typename T>
using id = T;
template<typename T, int N>
operator id<T[N]> &() {
...
}
};
Unlike the identity and typedef approach, this allows T and N to be deduced, i think.
C++ provides no syntax for that. This is one of those cases when you have to use a typedef-name for the type.
In order to avoid polluting the namespace, it is perfectly OK to declare the typedef-name inside the class
struct S {
int a[4];
typedef int A[4];
operator A&() { return a; }
};