Вызов функции члена неактивного члена союза - PullRequest
2 голосов
/ 29 мая 2020

Приводит ли вызов foo в следующем коде к UB?

using vec = std::array<int, 1>;
struct field0 {
    vec data;
    operator int() {
        return data[0];
    }
};

union a {
    struct {
        vec data;
    } data;
    field0 x;
};

void foo() {
    a bar;
    std::cin >> bar.data.data[0];
    std::cout << bar.x;
}

Согласно стандарту, x и data имеют одинаковый адрес, поэтому приведение * 1007 должно быть безопасным * на vec*. Кроме того, field0 и vec совместимы с макетом, поэтому можно безопасно проверять data через x.data или наоборот.

Однако мы не просто проверяем x.data, мы вызывать нестатическую c функцию-член x за пределами его времени жизни (или мы? Я не могу найти причину, по которой должно было начаться x время жизни), поэтому формально это UB. Это правильно?

Я пытаюсь достичь четко определенной версии общего подхода к полю массива имен, например union a { int data[3]; int x, y, z};

UPD:

  1. К сожалению, при удалении ненужных деталей была потеряна совместимость макета между полем массива и "геттером". Сейчас восстановлено.

  2. Мне нужно будет использовать a значения для функции, которая принимает int*, поэтому идти в другом направлении - объявление полей и перегрузка operator[] - не вариант .

1 Ответ

4 голосов
/ 29 мая 2020

Ваш код имеет неопределенное поведение. Исходное правило общей последовательности здесь не поможет, поскольку вы не обращаетесь к общему члену, а вместо этого обращаетесь ко всему объекту, чтобы вызвать функцию-член 1 .

То, что я пытаюсь достичь, - это четко определенная версия общего подхода к полю массива имен, например union a { int data[3]; int x, y, z};

В C ++ это можно сделать с помощью перегрузка структуры и оператора. Вместо того, чтобы иметь массив и пытаться сопоставить его с отдельными членами, у вас есть отдельные члены, а затем вы притворяетесь, что ваш класс является массивом. Это выглядит так:

struct vec
{
    int x, y, z;
    int& operator[](size_t index)
    {
        switch(index)
        {
        case 0: return x;
        case 1: return y;
        case 2: return z;
        }
    }
};

1: для вызова функции-члена необходимо передать этот объект этой функции, как если бы он был первым параметром функции.

...