I have a variadic variant_callable class object that I want to use for a runtime polymorphism. Inside it uses a visitor pattern with std::variant.
However, I came by a rather strange behavior, that is object's destructor is called twice!.
#include <utility>
#include <variant>
#include <tuple>
namespace detail
{
template<typename... Impl>
class variadic_callable
{
public:
template<typename T>
constexpr explicit variadic_callable(T &&t) //
: varImpl_(std::forward<T>(t))
{}
variadic_callable(const variadic_callable &) = delete;
variadic_callable(variadic_callable &&) = delete;
template<typename... Args>
constexpr decltype(auto) operator()(Args &&...args) const
{
return std::visit(
[argsTuple = std::forward_as_tuple(args...)](const auto &visitor) {
return std::apply(
[&visitor](auto &&...args) {
return visitor(std::forward<decltype(args)>(args)...);
},
argsTuple);
},
varImpl_);
}
private:
std::variant<Impl...> varImpl_;
};
} // namespace detail
#include <string>
#include <iostream>
int main(int, char **)
{
struct callable
{
std::string str = "Long enough string to be allocated. Oceanic";
callable()
{
std::cout << "callable()" << std::endl;
}
void operator()(int i) const
{
std::cout << str << " " << i << '\n';
}
~callable()
{
std::cout << "~callable()" << std::endl;
}
};
{
std::cout << "expcected:\n";
const auto &c = callable();
c(815);
std::cout << "finished\n";
}
std::cout << '\n';
{
std::cout << "actual\n";
const auto &w = detail::variadic_callable<callable>{callable()};
w(815);
std::cout << "finished\n";
}
}
The output:
Program returned: 0
expcected:
callable()
Long enough string to be allocated. Oceanic 815
finished
~callable()
actual
callable()
~callable()
Long enough string to be allocated. Oceanic 815
finished
~callable()
https://godbolt.org/z/d849EaqbE
I guess an UB is in-place, but I can't spot it.
What I find the most peculiar is the fact that in the "actual" case std::string resources are not destroyed after the first destructor invocation!
variadic_callable's constructor is being passed an object of type callable. This is a temporary object that cannot be the same object as the one stored in the std::variant (no matter how it is passed).
The callable inside the std::variant must therefore be move-constructed from the passed temporary object. Both of these objects need to be eventually destroyed, requiring two calls to callable's destructor.
To prevent this you need to pass the arguments from which callable is supposed to be constructed to variadic_callable's constructor instead (here an empty list) and then pass these on to std::variants in-place constructor, i.e.
template<typename T, typename... Args>
constexpr explicit variadic_callable(std::in_place_type_t<T> t, Args&&... args) //
: varImpl_(t, std::forward<Args>(args)...)
{}
called as
detail::variadic_callable<callable>{std::in_place_type<callable>};
Here I copied std::variant's constructor design for the in-place overload.
I wanted to write my own code to iterate over an n dimensional vector (where the dimension is known). Here is the code:
void printing(const auto& i, const int dimension){
int k= dimension;
for(const auto& j: i){
if(k>1){
cout<<"k: "<<k<<endl;
printing(j, --k);
}
else{
//start printing
cout<<setw(3);
cout<<j; //not quite sure about this line
}
cout<<'\n';
}
}
I get an error:
main.cpp:21:5: error: ‘begin’ was not declared in this scope
for(const auto& j: i){
^~~
Could someone help me to correct it or give me a better way to print the vector?
Thanks in advance for your time.
If the dimensions are known at compile-time, this can be solved easily with a template that takes dimensions as the non-type argument.
template <std::size_t Dimensions>
void printing(const auto& i){
if constexpr (Dimensions != 0) {
for(const auto& j: i){
// I'm not sure if it is intentional to print 'k' each iteration,
// but this is kept for consistency with the question
cout<<"k: " << Dimensions << endl;
printing<Dimensions - 1u>(j);
}
} else {
cout << setw(3);
cout << j;
cout << '\n';
}
}
The use would be, for a 2d vector:
printing<2>(vec);
Live Example
However, if you always know that const auto& i will be a std::vector type, you can potentially solve this even easier by just not using auto arguments at all, and instead use template matching:
// called only for the vector values
template <typename T>
void printing(const std::vector<T>& i){
for(const auto& j: i){
// possibly compute 'k' to print -- see below
printing(j);
}
}
// Only called for non-vector values
template <typename T>
void printing(const T& v) {
cout << setw(3);
cout << v;
cout << '\n';
}
Live Example
To compute the "dimension" of the vector, you can write a recursive type-trait for that:
#include <type_traits> // std::integral_constant
// Base case: return the count
template <std::size_t Count, typename T>
struct vector_dimension_impl
: std::integral_constant<std::size_t, Count> {};
// Recursive case: add 1 to the count, and check inner type
template <std::size_t Count, typename T, typename Allocator>
struct vector_dimension_impl<Count, std::vector<T,Allocator>>
: vector_dimension_impl<Count + 1u, T> {};
// Dispatcher
template <typename T>
struct vector_dimension : vector_dimension_impl<0u, T> {};
// Convenience access
template <typename T>
inline constexpr auto vector_dimension_v = vector_dimension<T>::value;
// Simple tests:
static_assert(vector_dimension_v<std::vector<int>> == 1u);
static_assert(vector_dimension_v<std::vector<std::vector<int>>> == 2u);
static_assert(vector_dimension_v<std::vector<std::vector<std::vector<int>>>> == 3u);
Live Example
With the above recursive trait, you can get the "dimension" of each templated vector type, without requiring the user to pass in the value at all.
If you still wanted to print k: each time, you can use the above simply with:
cout << "k: " << vector_dimension_v<T> << endl;
This only works if the type is known to be a vector -- but it could be written using concepts to work with anything following the abstract definition of something like a vector as well.
If you want this to work with any range-like type, then you could replace the vector-overload with a requires(std::ranges::range<T>) instead, and change the template-specializations for finding the dimension to also use the same. I won't pollute the answer with all this code since it's largely the same as above -- but I'll link to it in action below:
Live Example
I have made a function that can print any n-dimensional iterable container:
template<typename Object, typename Iterable>
void Print(
const Iterable& iterable,
const string& separatorDimensions = "\n",
const function<void(const Object&)>& funcPrintElem = [] (const Object& obj) {
static_assert(
is_arithmetic_v<Object> || is_same_v<remove_const_t<remove_pointer_t<Object>>, char>,
R"(The object from the innermost range is not a built-in/c-string type, please provide a valid print element function.)"
);
cout << obj << ' ';
}
) {
if constexpr (ranges::range<Iterable>) {
ranges::for_each(iterable, [&] (const auto& it) { Print(it, separatorDimensions, funcPrintElem); });
cout << separatorDimensions;
} else {
funcPrintElem(iterable);
}
}
The function has a default std::function that can print any built-in type like int, unsigned char, long long etc... and the c-string like char* or const char*, if you have another object like a pair or tuple or an object of your class you can pass a function that prints your object.
You can use the function like this: (you must explicitly tell the function your inner most object like below)
int main() {
cout << "v: " << endl;
vector<uint16_t> v { 1, 2, 3 };
Print<uint16_t>(v);
cout << endl << "ll: " << endl;
list<list<const char*>> ll { { "a", "b" }, { "c", "d" } };
Print<const char*>(ll);
struct smth {
int a;
char b;
};
cout << endl << "smths: " << endl;
vector<smth> smths { { 14, '0' }, { 18, '1' } };
Print<smth>(smths, "\n", [] (const smth& obj) { cout << "a = " << obj.a << ", b = " << obj.b << endl; });
return 0;
}
The function can be found here, maybe I will update in the future to support more things.
Edit: You need to have at least c++20 for this function to work
This has been asked before, but I'm curious to see if anything has changed in newer C++ standards. Any current or future standard is acceptable.
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, with elements stored on the stack, and doesn't require a 'make_array' function.
template<class T, size_t N>
struct Array
{
T items[N];
};
Array<int> foo = { 1, 2, 3 };
Since initializer_list is not templated on the size, a constructor using it won't do the job. Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Array foo = { 1, 2, 3 }; // Works
Array<int> foo = { 1, 2, 3 }; // Doesn't work
Array foo = { 1.0, 2.0, 3.0f }; //Doesn't work
A constructor that takes a c-array doesn't appear to work because an initializer_list won't convert to a c-array.
Is the braced-init-list to T[N] that happens in int foo[] = { 1, 2, 3 }; purely compiler magic that can't be replicated in code?
EDIT: The spirit of this question is about the exact syntax above. No make_array, no extra template argument, explicit item type, no double braces, no dynamic allocations. If a trivial Array requires a bunch of modern C++ tomfoolery and still can't manage to support standard syntax then it's just a bad engineering trade-off in my opinion.
You can work around the need for all the types in the list to be the same by using an explicit deduction guide:
template <class... T>
Array(T&&... t) -> Array<std::common_type_t<T...>, sizeof...(T)>;
Array foo = { 1.0, 2.0, 3.0f }; // Deduces Array<double,3u>
I hate to be the bearer of bad news, but I believe that at the current time (at least as of C++17) the answer to your question is "no" there is no way that would meet all of your given requirements. That said, there are solutions posted here that come close, but they all fall short in one or more of your requirements.
I also suspect that the answer will remain "no" in the near future. Not that I'm a prophet, but the direction of the last few C++ versions would seem to imply that a make_array solution is more likely to be what is added rather than more direct language support.
Explanation
Allow me to explain why in a little more detail.
First consider the C++17 deduction guides. I won't go into detail on them as they are discussed suitably by other answers on this question. They do come very close to what you are asking but do seem to fall short in one way or the other. (Although the answer by #max66 would seem to meet all your requirements except for the extra brackets. If his syntax in fact works you may want to consider that answer as "close enough".)
Next consider a variatic templates solution. In order to automatically determine N you would need a series of overloaded functions (basically one with a single argument and one that takes a single argument and the rest of the variatic templates). But that would essentially be equivalent to some form of make_array so it doesn't count either.
Finally, the only other option I could see would be based on an initializer_list. The question is how to determine N from that list. In C++11 this would be obviously impossible since the only access to the size of the list is const and not constexpr. However as of C++14 the size() method is in fact constexpr so you would think it is at least theoretically possible to get the compiler to deduce N based on that. Unfortunately to do this you would need to make N (a template parameter) default to a value of something in the class constructor (the initializer list). I have been unable to determine any means of doing this in the present form of the language.
How I think a future version could support this
One way to support this would be to follow the examples of other languages and add direct language support, bridging a syntax to certain classes. But this essentially makes some classes "special". Consider the following line in Swift:
let ar = [1, 2, 3, 4]
In this example ar is an object of the type Array<Int>. But this is done by direct compiler support, i.e. "Array" is a special case. No matter what you do you could not write a MyArray class that would perform the same way (other than perhaps having MyArray accept an Array as a construction option). It is certainly possible for the C++ standard to be extended to do something similar, but C++ tends to try to avoid those "special" cases. Besides, an argument could be made that
auto ar = make_array(1, 2, 3, 4);
is in fact a clearer representation of the intent than would be something like the following: (suggesting the character 'A' to distinguish this from an initializer list)
auto ar = A{ 1, 2, 3, 4 };
Another way, more in line with current C++ syntax, to do this would be to add the N parameter to the initializer_list template class. After all since size() is now constexpr the size must be known at compile time, so why not make it available as a template parameter? It could suitably default so that it could rarely be needed, but it would allow classes (both standard ones like std::array and custom ones like you are proposing) the possibility of tying the N in the Array template to the N in the initializer_list. Then you should be able to write something along the following lines:
template<class T, size_t N>
struct Array
{
explicit Array(std::initializer_list<N> il);
}
Of course the trick would be to make this initializer_list change in a fashion that does not break a lot of existing code.
I suspect the standards committee will not follow either of these paths, but will more likely add the experimental make_array method. And I'm not convinced that is a bad idea. We are used to make_... in many other parts of the language, so why not here as well?
Here's one approach that allows you to specify the type. We use an extra argument to specify the type, so that we can still use the deduction guide to pick up the size. I don't consider it pretty though:
#include <iostream>
template <typename T>
struct Tag { };
template <typename T, size_t N>
struct Array {
T data_[N];
template <typename... U>
Array(Tag<T>, U... u)
: data_{static_cast<T>(u)...} // cast to shut up narrowing conversions - bad idea??
{}
};
template <typename T, typename... U>
Array(Tag<T>, U...) -> Array<T, sizeof...(U)>;
int main()
{
Array a{Tag<double>{}, 1, 2.0f, 3.0};
for (auto d : a.data_) {
std::cout << d << '\n';
}
}
This is clearly not a full implementation of such a class, just to illustrate the technique.
Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Not necessarily.
Yes, you can't explicit the type parameter, but you can decide it according the types of the items.
I imagine two reasonable strategies: (1) the type of the Array is the type of the first item, following the std::array way, so writing the following deduction guide
template <typename T, typename ... Us>
Array(T, Us...) -> Array<T, 1u + sizeof...(Us)>;
(but observe that a C++ program where a Us type is different from T, for a std::array, is ill formed) or (2) follows the metalfox's suggestion and select the item's common type
template <typename ... Ts>
Array(Ts...) -> Array<std::common_type_t<Ts...>, sizeof...(Ts)>;
Is the braced-init-list to T[N] that happens in int foo[] = { 1, 2, 3 }; purely compiler magic that can't be replicated in code?
Are you thinking in a deduction guide as follows ?
template <typename T, std::size_t N>
Array(T const (&)[N]) -> Array<T, N>;
Works but with a couple of drawbacks: (1) you have to add a couple of brackets using it
// added ---V V--- added
Array foo1 = { { 1, 2, 3 } }; // Works
and (2) remain the problem that all items must have the same type
Array foo2 = { {1.0, 2.0, 3.0f} }; //Doesn't work: incompatible types
or the compiler can't deduce the type T
P.s.: what's wrong with a make_array() function ?
P.s.2: I suggest to give a look at BoBTFish's answer to see a nice method to bypass the impossibility to explicit a template argument using deduction guides.
You had asked this:
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, and without a 'make_array' function.
I have worked on an implementation of a class that behaves in the manner you are describing only it has a little bit more complexity to it but still fairly simple, readable, reusable, portable and generic.
I was not able to have a T items[] array as a direct member in the class. I had to use a T* items instead and create an overloaded operator[] in the derived class to mimic the behavior of an array. This doesn't mean that there isn't any work around to this as others have shown. I just find that this is one possible solution without having to specify the size of the array.
I use a base class to store the elements from the constructor either by means of an std::initializer_list or of a variadic constructor. The class templates themselves are not a variadic template only their constructor's are. The base class stores the values from the initializer_list or parameter pack into an std::vector. The inherited class stores the contents from the vector into T* by calling the data() function of the vector class.
template<typename T>
class ParamPack {
protected:
std::vector<T> values_;
size_t size_;
public:
template<typename... U>
ParamPack( U... u ) :
values_{ static_cast<T>(u)... },
size_( sizeof...(U) ) {}
template<typename ... U>
ParamPack( std::initializer_list<std::is_same<T, U...>( U...)> il ) :
values_( il ), size_( il.size() ) {}
std::vector<T>& operator()() { return values_; }
size_t size() const { return size_; }
};
template<typename T>
class Array : public ParamPack<T> {
private:
T* items_;
public:
template<typename... U>
Array( U... u ) : ParamPack<T>::ParamPack( u... ) {
items_ = this->values_.data();
}
template<typename... U>
Array( std::initializer_list<U...> il ) : ParamPack<T>::ParamPack( il ) {
items_ = this->values_.data();
}
T& operator[]( size_t idx ) {
return items_[idx];
}
T operator[]( size_t idx ) const {
return items_[idx];
}
T* data() const { return items_; }
};
int main() {
try {
// Parameter Pack Examples:
// Variadic Constructor { ... }
std::cout << "ParamPack<T> Examples:\n";
std::cout << "Using ParamPack<T>'s Variadic Constructor\n";
ParamPack<int> pp1( 1, 2, 3, 4 );
std::cout << "Size: " << pp1.size() << " | Elements: ";
for( auto& v : pp1() ) {
std::cout << v << " ";
}
std::cout << '\n';
std::cout << "Using ParamPack<T>'s Variadic Constructor with an Initializer List\n";
ParamPack<int> pp2( { 5, 6, 7, 8 } );
std::cout << "Size: " << pp2.size() << " | Elements: ";
for( auto& v : pp2() ) {
std::cout << v << " ";
}
std::cout << '\n';
std::cout << "Using ParamPack<T>'s initializer_list constructor\n";
std::initializer_list<int> il{ 9,10,11,12 };
ParamPack<int> pp3( il );
std::cout << "Size: " << pp3.size() << " | Elements: ";
for( auto& v : pp3() ) {
std::cout << v << " ";
}
std::cout << "\n\n";
// Array Examples:
std::cout << "Array<T> Examples:\n";
std::cout << "Using Array<T>'s initializer_list Constructor\n";
Array<int> arr( il );
for( size_t i = 0; i < arr.size(); i++ ) {
std::cout << arr[i] << " ";
}
std::cout << "\n";
// Using Variadic Constructor
std::cout << "Using Array<T>'s Variadic Constructor\n";
Array<int> testA( 9, 8, 7, 6 );
for( size_t i = 0; i < testA.size(); i++ ) {
std::cout << testA[i] << " ";
}
std::cout << '\n';
Array<std::string> testB( "Hello", " World" );
for( size_t i = 0; i < testB.size(); i++ ) {
std::cout << testB[i] << " ";
}
std::cout << "\n\n";
// Using Constructor w/ Initializer List
std::cout << "Using Array<T>'s Variadic Constructor with Initializer List\n";
Array<int> testC( { 105, 210, 420 } );
for( size_t i = 0; i < testC.size(); i++ ) {
std::cout << testC[i] << " ";
}
std::cout << "\n\n";
// Using Initializer List with =
std::cout << "Using Array<T>'s Initializer List with =\n";
Array<int> a = { 1, 2, 3, 4 };
for( size_t i = 0; i < a.size(); i++ ) {
std::cout << a[i] << " ";
}
std::cout << '\n';
Array<char> b = { 'a', 'b', 'c', 'd' };
for ( size_t i = 0; i < b.size(); i++ ) {
std::cout << b[i] << " ";
}
std::cout << '\n';
Array<double> c = { 1.2, 3.4, 4.5, 6.7 };
for( size_t i = 0; i < c.size(); i++ ) {
std::cout << c[i] << " ";
}
std::cout << "\n\n";
// Using Initializer List directly
std::cout << "Using Array<T>'s Initalizer List directly\n";
Array<uint32_t> a1{ 3, 6, 9, 12 };
for( size_t i = 0; i < a1.size(); i++ ) {
std::cout << a1[i] << " ";
}
std::cout << "\n\n";
// Using user defined data type
struct Point {
int x_, y_;
Point( int x, int y ) : x_( x ), y_( y ) {}
};
Point p1( 1, 2 ), p2( 3, 4 ), p3( 5, 6 );
// Variadic Constructor
std::cout << "Using Array<T>'s Variadic Consturctor with user data type\n";
Array<Point> d1( p1, p2, p3 );
for( size_t i = 0; i < d1.size(); i++ ) {
std::cout << "(" << d1[i].x_ << "," << d1[i].y_ << ") ";
}
std::cout << '\n';
// Initializer List Construtor (reversed order)
std::cout << "Using Array<T>'s Initializer List Constructor with user data type\n";
Array<Point> d2( { p3, p2, p1 } );
for( size_t i = 0; i < d2.size(); i++ ) {
std::cout << "(" << d2[i].x_ << "," << d2[i].y_ << ") ";
}
std::cout << '\n';
// Initializer List Version = {...} p2 first
std::cout << "Using Array<T>'s = Initializer List with user data type\n";
Array<Point> d3 = { p2, p1, p3 };
for( size_t i = 0; i < d3.size(); i++ ) {
std::cout << "(" << d3[i].x_ << "," << d3[i].y_ << ") ";
}
std::cout << '\n';
// Initializer List Directly p2 first p1 & p3 swapped
std::cout << "Using Array<T>'s Initializer List directly with user data type\n";
Array<Point> d4{ p2, p3, p1 };
for( size_t i = 0; i < d4.size(); i++ ) {
std::cout << "(" << d4[i].x_ << "," << d4[i].y_ << ") ";
}
std::cout << '\n';
std::initializer_list<Point> ilPoints{ p1, p2, p3 };
std::cout << "Using Array<T>'s initializer_list Constructor with user data type\n";
Array<Point> d5( ilPoints );
for( size_t i = 0; i < d5.size(); i++ ) {
std::cout << "(" << d5[i].x_ << "," << d5[i].y_ << ") ";
}
std::cout << "\n\n";
// Need a local copy of the vector instead?
std::cout << "Using Array<T>'s base class's operator()() to retrieve vector\n";
std::vector<Point> points = d4(); // using operator()()
for( auto& p : points ) {
std::cout << "(" << p.x_ << "," << p.y_ << ") ";
}
std::cout << '\n';
// Need a local copy of the pointer instead?
std::cout << "Using Array<T>'s data() to get the contents of its internal pointer\n";
Point* pPoint = nullptr;
pPoint = d4.data();
for( size_t i = 0; i < d4.size(); i++ ) {
std::cout << "(" << pPoint[i].x_ << "," << pPoint[i].y_ << ") ";
}
std::cout << '\n';
} catch( const std::runtime_error& e ) {
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
-Output-
ParamPack<T> Examples:
Using ParamPack<T>'s Variadic Constructor
Size: 4 | Elements: 1 2 3 4
Using ParamPack<T>'s Variadic Constructor with an Initializer List
Size: 4 | Elements: 5 6 7 8
Using ParamPack<T>'s initializer_list Constructor
Size: 4 | Elements: 9 10 11 12
Array<T> Examples:
Using Array<T>'s initializer_list Constructor
9 10 12 12
Using Array<T>'s Variadic Constructor
9 8 7 6
Hello World
Using Array<T>'s Constructor with Initializer List
105 210 420
Using Array<T>'s Initializer List with =
1 2 3 4
a b c d
1.2 3.4 5.6 7.8
Using Array<T>'s Initializer List directly
3 6 9 12
Using Array<T>'s Variadic Constructor with user data type
(1,2) (3,4) (5,6)
Using Array<T>'s Variadic Constructor With Initializer List of user data type
(5,6) (3,4) (1,2)
Using Array<T>'s = Initializer List with user data type
(3,4) (1,2) (5,6)
Using Array<T>'s Initializer List directly with user data type
(3,4) (5,6) (1,2)
Using Array<T>'s initializer_list Constructor with user data type
Using Array<T>'s base class's operator()() to retrieve vector
(3,4) (5,6) (1,2)
Using Array<T>'s data() to get the contents of its internal pointer
(3,4) (5,6) (1,2)
Now this is a little more robust as it has operators that are available from both the parent and child class, from the parent class you can get the stored vector directly from its operator()(). From the child class you can index into the child's stored pointer from operator[]() and there is a function to return it's size. The template itself does not contain a size_t N template argument since the size is stored internally in the base class and is determined by the size of its vector. With this I'm treating T* p as if it was T p[size]. This class is still not without some limitations.
-Valid Construction-
Array<int> a( 1, 2, 3, 4 ); // Variadic Constructor Okay
Array<int> a( {1,2,3,4} ); // Initializer List Constructor Okay
Array<int> a = { 1, 2, 3, 4 }; // Initializer List Okay
Array<int> a{ 1,2,3,4 }; // Initializer List Okay
-Limitations-
However you have to explicitly instantiate the template as these will not work as they all generate a compiler error.
Array a( 1,2,3,4 );
Array a( {1,2,3,4} );
Array a = { 1, 2, 3, 4 };
Array a{ 1,2,3,4 };
-Note- There maybe more that can be done to make this more efficient, maybe even thread safe or exception safe, but this is just a generalization of the class design.
Please let me know what you think about this:
So I have two classes containing std::map members with effectively identical functionality except that the ordering of one map is std::less and the other std::greater.
If I create an abstract parent class and declare a single map member, is there any way to dynamically assign the comparator for this member in the derived class constructors? That way the functionality can obviously all reside in the parent class.
You can't change the comparator after the fact. But you can use the same comparator class and get either "greater" or "less" at the time of construction. You just need a stateful comparator:
struct my_compare {
enum compare_type { less, greater };
explicit my_compare(compare_type t) : m_type(t) {}
template<class T, class U>
bool operator()(const T& t, const U& u) const {
if(m_type == less) { return t < u; }
else { return t > u; }
}
compare_type m_type;
};
Then you can do
std::map<int, int, my_compare> less_map((my_compare(my_compare::less)));
std::map<int, int, my_compare> greater_map((my_compare(my_compare::greater)));
The extra pair of parentheses is because it would otherwise be the most vexing parse , even though a function parameter declaration cannot have a qualified name. In C++11, list-initialization (my_compare{mycompare::less}) can be used instead.
For your specific design, an implementation might look like
class A {
protected:
explicit A(my_compare::compare_type ct) : my_map(my_compare(ct)) {}
std::map<int, int, my_compare> my_map;
};
class B_less : public A{
public:
B_less() : A(my_compare::less) {}
};
No. The comparison function is used to generate the actual data structure -- changing the comparison function would require rebuilding the structure from scratch.
That said, if all you want to do is iterate the structure in reverse order, map is a reversible container, so you can just loop over the structure normally using the reverse iterators.
You can do what you want to do by creating a custom functor class that uses less or greater depending on some state. Here's an example:
#include <iostream>
#include <string>
#include <map>
struct MyCompare
{
MyCompare(bool useLess) : useLess_(useLess) {}
bool operator()(int lhs, int rhs)
{
if ( useLess_ )
{
return (lhs < rhs);
}
else
{
return (lhs > rhs);
}
}
bool useLess_;
};
int main(int argc, char** argv)
{
std::map<int, std::string, MyCompare> myMap1(MyCompare(true));
std::map<int, std::string, MyCompare> myMap2(MyCompare(false));
myMap1[1] = "abcd";
myMap1[2] = "lmnop";
myMap1[3] = "xyz";
myMap2[1] = "abcd";
myMap2[2] = "lmnop";
myMap2[3] = "xyz";
std::cout << "Map 1: " << std::endl; for ( auto const& v : myMap1 )
{
std::cout << "Key: " << v.first << ", Value: " << v.second << std::endl;
}
std::cout << "Map 2: " << std::endl;
for ( auto const& v : myMap2 )
{
std::cout << "Key: " << v.first << ", Value: " << v.second << std::endl;
}
return 0;
}
Output:
Map 1:
Key: 1, Value: abcd
Key: 2, Value: lmnop
Key: 3, Value: xyz
Map 2:
Key: 3, Value: xyz
Key: 2, Value: lmnop
Key: 1, Value: abcd
In your case, you can pass a flag from the child class to the parent class indicating what value to use to create the compare functor.