LINUX.ORG.RU

Ищу нормальную инициализацию константных структур

 ,


1

3

Нужен аналог растовского:

#[derive(Debug)]
struct Data {
    a: i32,
    b: i32,
    c: i32,
    flag: bool,
}

fn main() {
    let d = Data {
        a: 1,
        b: 1,
        c: 1,
        flag: false,
    };
        
    println!("{:?}", d);   
}

То есть:

  1. Значение присваивают по имени.
  2. Структура иммутабельная, ака все поля const.
  3. Пропустить инициализацию поля нельзя - ошибка компиляции.

Примеры:

struct Data
{
    const int a;
    const int b;
    const int c;
    const bool flag;
};

Data d1 {.a = 1}; // компилятор вообще молчит
Data d2 {1}; // warning: missing field 'b' initializer, только одно проверят; легко ошибиться полем
qDebug() << d1.b; 
qDebug() << d2.b; 

Есть решения?

★★★★★

Последнее исправление: RazrFalcon (всего исправлений: 1)

Ответ на: комментарий от xaizek

Да, точно. Весь стандарт C++11 так и не осилил. В любом случае это левые значения.

RazrFalcon ★★★★★
() автор топика
Последнее исправление: RazrFalcon (всего исправлений: 1)
#include <iostream>
#include <iomanip>

struct Data
{
    const int a;
    const int b;
    const int c;
    const bool flag;

    Data(int na, int nb, int nc, bool nflag):
      a(na), b(nb), c(nc), flag(nflag)
    {}
};

int main()
{
    Data d1(1, 2, 3, false);

    std::cout << "A=" << d1.a << std::endl;

    return 0;
}
Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 2)

По поводу константных значений, которые обязательно должны быть проинициализированны, можно сделать шаблон вроде такого:

template<typename T>
class const_value {
  const T v_;
public:
  const_value() = delete; // Это можно и не писать.
  const_value(T v) : v_(std::move(v)) {}

  const T & get() const { return v_; }
};
Тогда:
struct Data {
  const_value<int> a;
  const_value<int> b;
  const_value<int> c;
  const_value<bool> flag;
};

По поводу того, чтобы можно было записывать инициализатор по имени, такого не припомню. Здесь вам нужно будет, скорее всего, что-то вокруг паттерна Builder выстраивать.

eao197 ★★★★★
()
Ответ на: комментарий от RazrFalcon

См. требование №1.

Да, этому не удовлетворяет.
Я сам жду когда же завезут именованные параметры в функции: очень удобно когда несколько аргументов с дефолтными аргументами

Но если «Пропустить инициализацию поля нельзя», то зачем тебе «Значение присваивают по имени»?

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Но если «Пропустить инициализацию поля нельзя», то зачем тебе «Значение присваивают по имени»?

Kroz ★★★★★
()
Ответ на: комментарий от Kroz

Если у нас есть одинаковые типы, то легко попутать. В примере с rust ошибиться не выйдет.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon

А в чем разница между этим шаблоном и const?

Упс... Наверное, никакой. Это я с чем-то другим перепутал.

Builder будет очень неэффективным же.

Вовсе не факт. Компиляторы сейчас могут очень многое выбрасывать при оптимизации.

eao197 ★★★★★
()

Не это ли тебе нужно?

$ сat test.cpp
#include <iostream>
#include <iomanip>

struct Data
{
  const int a;
  const int b;
};

int main()
{
  Data d1{a:1, b:2};

  std::cout << "A=" << d1.a << ", B=" << d1.b << std::endl;

  Data d2{a:2};
  std::cout << "A=" << d2.a << ", B=" << d2.b << std::endl;

  return 0;
}
[s]kroz@lix:~/tmp/delem> g++ -Wextra -std=c++1z ./test.cpp -o test && ./test
./test.cpp: In function ‘int main()’:
./test.cpp:17:14: warning: missing initializer for member ‘Data::b’ [-Wmissing-field-initializers]
   Data d2{a:2};
              ^
A=1, B=2
A=2, B=0

Нюансы:
- всё равно нужно параметры ставить в нужном порядке.
- по идее gcc-specific
Но это не существенно для твоей задачи, так?

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 2)

Две темы за менее чем 4 часа и обе меня радуют тем, как аффтар выдумывает проблему и начинает городить эпические конструкции для её решения. И всё это потому, что c++ убогий недоязык, где постоянно утекает память и везде висячие ссылки, а вовсе не потому что за клавиатурой сидит макака.

Вотя смотрю на растаманов и не понимаю почему вы на расте не пишете-то? Зачем ныть «хочу как в расте» но не пользоваться им?

ckotinko ☆☆☆
()

Ну вот в порядке бреда и экспериментов с шаблонами:

#include <utility>

template<typename Tag, typename V>
class tmp_value {
	template<typename Tag1, typename V1> friend class const_value;
	V v_;
public:
	explicit tmp_value(V v) : v_(std::move(v)) {}
};

template<typename Tag, typename V>
class const_value {
	const V v_;
public :
	explicit const_value(V v) : v_(std::move(v)) {}
	const_value(tmp_value<Tag, V> && tmp) : v_(std::move(tmp.v_)) {}

	const V & get() const { return v_; }
	const V & operator*() const { return get(); }
};

template<typename T, typename V>
auto make(V v) { return tmp_value<T, V>(std::move(v)); }

namespace tags {

struct A {};
struct B {};
struct C {};
struct Flag {};

}

struct Data {
	const_value<tags::A, int> a_;
	const_value<tags::B, int> b_;
	const_value<tags::C, int> c_;
	const_value<tags::Flag, bool> flag_;

	Data(
		tmp_value<tags::A, int> && a,
		tmp_value<tags::B, int> && b,
		tmp_value<tags::C, int> && c,
		tmp_value<tags::Flag, bool> && flag)
		: a_(std::move(a))
		, b_(std::move(b))
		, c_(std::move(c))
		, flag_(std::move(flag))
	{}
};

int main() {
	Data d( make<tags::A>(0), make<tags::B>(1), make<tags::C>(2), make<tags::Flag>(false) );
}

Суть в том, что для каждого поля структуры заводится свой уникальный тип-тег. Тогда любая инициализация этого поля должна быть явно помечена этим же типом-тегом. Понятное дело, что в ран-тайме типы-теги вообще не присутствуют.

В принципе, если все константы будут примитивными типами, для которых std::move не имеет эффекта, то можно вообще обойтись без tmp_value и make(). Но если константами будут std::string или какой-нибудь std::vector, то tmp_value позволит избежать лишнего копирования.

eao197 ★★★★★
()
Ответ на: комментарий от ckotinko

убогий не язык а аффтар, который постоянно генерит хотелки, после чего в треид подтягиваются другие убогие, которые начинают хаять С++

anonymous
()
Ответ на: комментарий от ckotinko

Сеньор обезьян, покажете же мне как решить поставленную задачу.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon

Типа оно, но увы:

Какой у тебя компилятор? У меня:

$ g++ --version
g++ (Gentoo 6.3.0 p1.0) 6.3.0

mainwindow.cpp:67: warning: use of GNU old-style field designator extension [-Wgnu-designator]

Ну так используй .a как тебе и написали. Это не важно. Решение проблемы состоит в -Wextra или -Wmissing-field-initializers

Kroz ★★★★★
()

Попкорна нет, но только что пожарил картошку, и есть холодное домашнее молоко.

Надо было хотя бы одно поле сделать ссылкой, чтобы появились явные лайфтаймы, и пошла потеха.

Virtuos86 ★★★★★
()

Вообще-то в исходном примере компилятор говорит что-то вроде:

15:22: error: uninitialized const member 'main()::Data::b'
15:22: error: uninitialized const member 'main()::Data::c'
15:22: error: uninitialized const member 'main()::Data::flag'
16:17: error: uninitialized const member 'main()::Data::b'
16:17: error: uninitialized const member 'main()::Data::c'
16:17: error: uninitialized const member 'main()::Data::flag'

Только не понятно почему константы - в определениях полей структуры, а не в определении самой переменной, если уж тут говорится об аналогах.

#[derive(Debug)]
struct Data {
    a: i32,
    b: i32,
    c: i32,
    flag: bool,
}

fn main() {
    let mut d = Data {
        a: 1,
        b: 1,
        c: 1,
        flag: false,
    };
        
    println!("{:?}", d);
    
    d.a = 10;

    println!("{:?}", d);
    
}
asaw ★★★★★
()
Ответ на: комментарий от O02eg

Сырая альфа, которая ничего не умеет.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от Virtuos86

В чём потеха? В том, что С++ слил бы ещё сильнее?

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от asaw

Это и есть аналог, ибо d всегда будет константным, в моём примере. Так как его нельзя копировать.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon

Это и есть аналог, ибо d всегда будет константным, в моём примере. Так как его нельзя копировать.

А я разве про это спросил?

asaw ★★★★★
()
Ответ на: комментарий от asaw

А clang молчит...

Правда оба ругаются о том, что .a это фичи C99. Но на этом можно забить.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon

Загадочное объяснение. Я его понял так: «потому, что раст не умеет в константные поля структур».

asaw ★★★★★
()
Ответ на: комментарий от RazrFalcon

Его то ли уже приняли, то ли примут в 20-м. Пока - никак по-человечески.

anonymous
()

С макрами ты решил бы эту задачу тривиально :-) Без головоломок длиной в полдня, и без простыней тарабарщины длиной в 50 строк :-) Но тут... :-) В итоге ты смиришься с тем, что данная задача не имеет приемлемых решений и будешь довольствоваться тем, что тебе даёт цепепе :-) Или подождёшь каких-нибудь лет 8-12 (всего-то, время летит то быстро) :-) Лол :-)

anonymous
()
Ответ на: комментарий от eao197

Очень простое, изящное и лаконичное решение :-) Спасибо :-)

anonymous
()
Ответ на: комментарий от RazrFalcon

Всё просто: в текущем C++ - никак (кроме ужасных шаблонных игрищ с шаблонными тегами от eao197). Потом - появится возможность.

zamazan4ik ★★
()
Ответ на: комментарий от asaw

Не умеет. Но в данном случае оно и не нужно. Ибо если я создал константный объект - он всегда будет константным. Мне этого достаточно.

В C++ же он будет копироваться, что приведёт к потере константности, для чего и нужен const.

Конечно можно повозится с удалением конструкторов копирования и прочего, но это тоже морока.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon

В C++ же он будет копироваться, что приведёт к потере константности, для чего и нужен const.
Конечно можно повозится с удалением конструкторов копирования и прочего

Ничего не понял. Как создание копии константы может изменить оригинал? По-моему ты здесь путаешь типы объектов и экземпляры объектов.

asaw ★★★★★
()
Ответ на: комментарий от RazrFalcon

К чему тогда твой пример на расте?

#[derive(Debug)]
struct Data {
    a: i32,
    b: i32,
    c: i32,
    flag: bool,
}

fn main() {
    let d = Data {
        a: 1,
        b: 1,
        c: 1,
        flag: false,
    };
        
    println!("{:?}", d);
    
    let mut md = d;
    
    md.a = 10;
    
    println!("{:?}", md);
}
asaw ★★★★★
()
Ответ на: комментарий от asaw

Экий вы хитрый:

println!("{:?} {:?}", d, md);

error[E0382]: use of moved value: `d`
  --> src/main.rs:23:27
   |
19 |     let mut md = d;
   |         ------ value moved here
...
23 |     println!("{:?} {:?}", d, md);
   |                           ^ value used here after move
   |
   = note: move occurs because `d` has type `Data`, which does not implement the `Copy` trait

RazrFalcon ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.