6

Somethimes i need sets (or any other container) of 1 element of user's type and create them this way:

boost::assign::list_of(typeVariable).convert_to_container<std::unordered_set<UserType> >()

Do anyone know more beautiful way?

PS: For example we have any business logic API, which searches for elements, and it takes set (or another container) of types for selection. Different users have access to different types. Also we can select any one type for filtering, and in this case we will take this one type from filtering options.

So i just wanted the simple way to write code in 1 line. My current version is:

getElements(filter.type != UNDEFINED
            ? boost::assign::list_of(filter.type).convert_to_container<std::set<UserType> >()
            : std::set<UserType>(allowedTypes.begin(), allowedTypes.end()))
  • 5
    Side-question: why would you need a set of a single element? Do you expand that set later on or does it remain with a single item in it? – rbaleksandar Jun 01 '16 at 09:00
  • This seems to be a question regarding how to create a design flaw. You appear to be attempting to place a lot of logic in a single line of code, which makes the line less readable and thus less maintainable. – Zac Howland Jun 02 '16 at 01:48

4 Answers4

5
std::unordered_set<UsertType> set = {typeVariable}

Or for a fully working code including a set generated as a temporary:

#include <iostream>
#include <unordered_set>
using namespace std;

int main() {
    /* First (unused in the rest of the code) set */
    std::unordered_set<int> set = {1};

    /* Set generated on the fly, if you don't even want to create
       a variable for it */
    cout << *std::unordered_set<int>({2}).begin() << endl;

    return 0;
}
coyotte508
  • 9,175
  • 6
  • 44
  • 63
  • @pavelalexeenko why? `getElements(filter.type != UNDEFINED ? std::set({filter.type}): std::set(allowedTypes.begin(), allowedTypes.end()))` . There's nothing not standard about it. – coyotte508 Jun 01 '16 at 12:27
5

Unfortunately, in C++ you cannot simply use auto set={2.3};, because this creates not a set, but an std::initializer_list. However, with the help of

template<typename Tp>
inline std::unordered_set<Tp> make_set(Tp const&x)
{ return {x}; }

you can use

auto set = make_set(2.3);

without the need to specify the type of set explicitly (as in another answer).

You can extend make_set() to take any number of arguments of the same type (I leave this as an exercise for you), but it's easier to add an overload taking an initializer list

template<typename Tp>
std::unordered_set<Tp> make_set(std::initializer_list<Tp> const&x)
{ return {x}; }

when

auto set = make_set({2.3,3.1});

creates an std::unordered_set<double> with two elements.

Edit It seems to me that there is no pure std solution which avoids explicitly naming the type of the set (std::unordered_set<possibly_templated_user_type>). Thus, make_set() (or similar) is the only such solution and, arguably, should be provided by std (similar to std::make_unique() and std::make_shared()).

Walter
  • 44,150
  • 20
  • 113
  • 196
  • This is possibly the easiest way, but i'm still looking for standard library\boost decision. – pavelalexeenko Jun 01 '16 at 09:56
  • @bolov that's an interesting extension for C++17 avoiding to name the template parameter, making life much easier in many situations. Thus with supporting compilers you will be able to say `std::unordered_set{x}` – Walter Jun 01 '16 at 12:04
  • My c++11 solution does avoid naming the type of the set or element /cc @bolov – sehe Jun 01 '16 at 13:31
  • @sehe you instead manipulated the user side (`getElement()` which was only added to the PO in a later edit btw), essentially requiring substantial overloads. But I think the PO wants to call various similar functions. – Walter Jun 01 '16 at 18:20
2

Exploit the fact that pointers to objects can be used as single-element iterators:

auto x = f();
std::set<T> xs(&x, &x + 1);
  • 2
    Well, technically this is treating the object as a single-element array containing said object. The pointer is the actual iterator, not the pointed-to object. – MSalters Jun 01 '16 at 12:59
  • @bolov No, it is not undefined behavior. There is a special rule that allows this. http://stackoverflow.com/a/14505930/1804599 –  Jun 01 '16 at 14:53
  • @rightfold Nice find. If only I would have read the paragraph that was just before the one I referenced... mea culpa. – bolov Jun 01 '16 at 15:03
1

UPDATE Note that if the getElements function already takes an explicitly typed set<...> const& you can simply write:

if (...)
    getElements({ filter.type});
else
    getElements({ allowedTypes.begin(), allowedTypes.end() });

That uses C++ uniform initialization and initializer list syntax

Assuming you have the API like so:

template <typename Container>
void getElements(Container const& xs);

// usage:    
getElements(std::vector<int> {1, 2});
getElements(std::list<std::string> {"1", "2"});

I think you are looking for an extra interface like so:

template <typename V>
void getElements(std::initializer_list<V> const& xs) { 
    getElements(std::set<V>(xs));
}

DEMO

Live On Coliru

#include <set>
#include <vector>
#include <list>
#include <string>
#include <iostream>

template <typename Container>
void getElements(Container const& xs) { 
    std::cout << __PRETTY_FUNCTION__ << " ";
    for(auto& x : xs) std::cout << x << ";";
    std::cout << "\n";
}

template <typename V>
void getElements(std::initializer_list<V> const& xs) { 
    getElements(std::set<V>(xs));
}

int main() {
    getElements(std::vector<int> {1, 2});
    getElements(std::list<std::string> {"1", "2"});

    getElements({1});
    getElements({1, 2});
}

Prints

void getElements(const Container&) [with Container = std::vector<int>] 1;2;
void getElements(const Container&) [with Container = std::__cxx11::list<std::__cxx11::basic_string<char> >] 1;2;
void getElements(const Container&) [with Container = std::set<int, std::less<int>, std::allocator<int> >] 1;
void getElements(const Container&) [with Container = std::set<int, std::less<int>, std::allocator<int> >] 1;2;
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • This still cannot do the `getElement(filter.type != UNDEFINED? single_element : many_elements)` call required. – Walter Jun 01 '16 at 18:21