Using Local Classes With Stl Algorithms

Using local classes with STL algorithms

It's explicitly forbidden by the C++98/03 standard.

C++11 remove that restriction.

To be more complete :

The restrictions on types that are
used as template parameters are listed
in article 14.3.1 of the C++03 (and
C++98) standard:

A local type, a type with no linkage,
an unnamed type or a type compounded
from any of these types shall not be
used as a template-argument for a
template type-parameter.

template <class T> class Y { /* ... */  }; 
void func() {
struct S { /* ... */ }; //local class
Y< S > y1; // error: local type used as template-argument
Y< S* > y2; // error: pointer to local type used as template-argument }

Source and more details : http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=420

To sum up, the restriction was a mistake that would have been fixed sooner if the standard was evolving faster...

That said today most last versions of common compilers does allow it, along with providing lambda expressions.

Approaching STL algorithms, lambda, local classes and other approaches

Boost.Bind, Boost.Function, and Boost.Lambda are your friends.

Using functors with STL algorithms versus lambda expressions?

Yes you're right.

A lambda expression basically creates a nameless functor. It just allows the programmer to do it in less code. Before C++11 the standard algorithms could only work with functors which would require the programmer to set up a whole new class just for some specific behaviour (like you just did).

This is exactly the reason why lambda's were introduced into C++11, to make sure using the standard algorithms with a custom functor weren't such a pain to write anymore.

passing local class function pointer to std::list::sort

Example code showing operator overloading, a static function (call commented out), and a lambda function (call commented out):

#include <iostream>
#include <list>
#include <string>

class names
{
struct name_node
{
std::string name;
inline bool operator< (const name_node &rnode)
{return (this->name < rnode.name);}
static bool compare_node(const name_node & lnode, const name_node & rnode)
{return (lnode.name < rnode.name);}
};

std::list <name_node> name_list;

public:
void get_data();
void sort_data();
void show_data();
};

void names::get_data()
{
name_node nn;
std::string fruits[5] = { "peach", "cherry", "apple", "plum", "banana" };

for (size_t i = 0; i < sizeof(fruits) / sizeof(fruits[0]); i++){
nn.name = fruits[i];
name_list.push_back(nn);
}
}

void names::sort_data()
{
name_list.sort();
// name_list.sort(names::name_node::compare_node);
// name_list.sort([this](const name_node & lnode, const name_node & rnode)
// {return (lnode.name < rnode.name);});
}

void names::show_data()
{
std::list<name_node>::iterator it;
for(it = name_list.begin(); it != name_list.end(); ++it)
std::cout << (*it).name << std::endl;
};

int main()
{
names n;
n.get_data();
n.sort_data();
n.show_data();
return 0;
}

Composability of STL algorithms

Since C++20 you can use std::ranges::copy together with the range adaptors std::views::filter and std::views::values from the Ranges library as follows:

int main() {
std::vector<std::pair<int, int>> values = { {1,2}, {4,5}, {6,7}, {9,10} };
std::vector<int> result;

auto even = [](const auto& p) { return (p.first % 2) == 0; };
std::ranges::copy(values | std::views::filter(even) | std::views::values,
std::back_inserter(result));

for (int i : result)
std::cout << i << std::endl;

return 0;
}

Output:

5

7

In the solution above, no temporary vector is created for an intermediate result, because the view adaptors create ranges that don't contain elements. These ranges are just views over the input vector, but with a customized iteration behavior.

Code on Wandbox

Express early termination with STL algorithms

Here's one possible approach: compute sum as the side-effect of an algorithm that terminates early. e.g.

int sum = 0;
auto f = std::find_if(keys.begin(), keys.end(), [&](string const &key) {
if (my_map.find(key) == my_map.end())
return true;
sum += my_map[key];
});

return f == keys.end() ? make_unique(sum) : nullptr;

Using STL algorithms (specifically std::sort) from within a templated class

You can use a temporary local function pointer variable of the required type to select the correct overload of DataSpecificComparison:

void SortMyContainerObjects()
{
typedef bool (*comparer_t)(const T*, const T*);
comparer_t cmp = &DataSpecificComparison;
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), cmp);
}

Here the compiler can deduce that you want to use the DataSpecificComparison overload that matches the comparer_t type, which resolves the ambiguity.

How to bind lambdas for STL algorithms to C style multidimensional arrays?

In C++14, use const auto& in the lambda.

If you have to provide type explicitly:

static auto colLessThan = [] (const float (&lhs)[2], const float (&rhs)[2])
{
return lhs[1] < rhs[1];
};

float (*lhsColMinIt)[2];
float (*lhsColMaxIt)[2];
std::tie(lhsColMinIt, lhsColMaxIt) =
std::minmax_element(pix, pix + sizeToParse, colLessThan);

Demo

Could multiple proxy classes make up a STL-proof bitvector?

My question is: can this be remedied/mitigated when the
reference_proxy overloads the address-of operator& to return a
pointer_proxy?

libc++ actually does this.

#include <vector>
#include <cassert>

int main()
{
std::vector<bool> v(1);
std::vector<bool>::pointer pb = &v[0];
assert(*pb == false);
*pb = true;
assert(v[0] == true);
std::vector<bool>::const_pointer cbp = pb;
assert(*cbp == true);
v[0] = false;
assert(*cbp == false);
}

It even extends to const_pointer and const_reference in ways that mimic the same types for vector<int>. This is a non-conforming extension for libc++. But it makes writing generic code which might be instantiated on vector<bool> far more likely to compile and behave correctly.

Questions: would such a multi-proxy approach work with the STL's
algorithms? Or do some algorithms really rely on the requirement that
x needs to be a real bool*? Or are there too many consecutive
user-defined conversions required that prevent this to work?

All of libc++'s algorithms work with vector<bool>. Some of them with quite spectacular performance. One algorithm in particular must have special treatment which the standard unfortunately does not mandate:

#include <vector>
#include <cassert>

int main()
{
std::vector<bool> v(1);
bool b = true;
assert(v[0] == false);
assert(b == true);
std::swap(b, v[0]);
assert(v[0] == true);
assert(b == false);
}

This is very easy for the implementation to accomplish. One simply needs to make sure swap works for any combination of bool and vector<bool>::reference. But I don't know if any implementation besides libc++ does this, and it is not mandated by C++11.

An array of bits is a wonderful data structure. But unfortunately it is poorly specified in the C++ standard. libc++ has gone somewhat outlaw to demonstrate that this can be a very useful and high performance data structure. The hope is that a future C++ standard may migrate in this direction to the benefit of the C++ programmer.



Related Topics



Leave a reply



Submit