1

Is there any way of specifying arguments in order that is different from the order of declaration?

For example, let's say i have following struct:

struct Coord
    {
        int x, y;
    };

Can i initialize it as

auto a = Coord{ y(5),x(4) };

Note that I am asking this out of curiosity, I don't really need to do something like this. But I want to know if I can initialize a struct without declaring a constructor but with specifying arguments.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Eren
  • 316
  • 1
  • 9
  • 1
    You could do it in C: `struct Coord a = { .y = 5, .x = 4 };` – melpomene Mar 24 '19 at 11:06
  • @Eren There is no builtin feature for that. Though you could pull off something similar with elaborate templates. Search for libraries for "named arguments" on github, there are plenty of them. – HolyBlackCat Mar 24 '19 at 11:08
  • 3
    In C++, the members of a struct are always initialized in the order they're specified in the struct. You need to declare a constructor to use different parameters there. [Designated initializers](https://en.cppreference.com/w/cpp/language/aggregate_initialization#Designated_initializers) (what @melpomene mentioned) are coming in C++20, but they still don't allow you to specify a different order. – Daniel H Mar 24 '19 at 11:10

2 Answers2

7

Unfortunately you can't change the order of struct member construction. With C++20 you can specify the members by name (see designated initializers). Unfortunately the initialized names must be in the same order as the members.

Here is a quote from the cppreference (the link above):

Note: out-of-order designated initialization, nested designated initialization, mixing of designated initializers and regular initializers, and designated initialization of arrays are all supported in the C programming language, but are not allowed in C++.

struct Coord
{
    int x, y;
};

Initialization:

auto a = Coord{ .x=4, .y = 5}; // Ok with C++20

But this won't work (see the quote above):

auto a = Coord{ .y=5, .x = 4}; // ERROR

For a different order you must define a constructor, or a helper function.

Try it out at compiler explorer.

Michael Veksler
  • 8,217
  • 1
  • 20
  • 33
2

I'm not sure what you want to achieve, however, it looks like you want to prevent the swapping of X and Y or even automatically update the right member if it happens.

Jonathan Boccara has a nice blog post about that: https://www.fluentcpp.com/2016/12/08/strong-types-for-strong-interfaces/

The idea is that you write:

 using X = NamedType<int, struct XTag>;
 using Y = NamedType<int, struct YTag>;

struct Coord
    {
    X x;
    Y y;
    };

 auto a = Coord{ X(4),Y(5) };

If everything is default constructable and the types in your struct are unique, you can do something with variadic templates to assign the right value to the right member.

The constructor will look a bit like:

struct Coord
    {
    template<typename ...T>
    Coord(T &&...t)
        {
        assignByType(std::forward_as_tuple(x, y), std::forward<T>(t)...);
        }

    X x{0};
    Y y{0};
    };

Based on the types, you can assign the values and even assert that all types are in and that none are ignored.

An implementation of assignByType can be:

#include <utility>
#include <tuple>

template<typename ... T, typename ... U, typename V>
auto assignByTypeHelper(std::tuple<T &...> t, V &&v, U && ... u)
    {
    std::get<V &>(t) = std::forward<V>(v);
    if constexpr( sizeof...(U))
        {
        assignByTypeHelper(std::move(t), std::forward<U>(u)...);
        }
    }

template<typename ... T, typename ... U>
auto assignByType(std::tuple<T &...> t, U && ... u)
    {
    static_assert(sizeof...(T) == sizeof...(U));
    return assignByTypeHelper(std::move(t), std::forward<U>(u)...);
    }

Full code at compiler explorer

JVApen
  • 11,008
  • 5
  • 31
  • 67