Почему я не могу сделать внешний тип похожим на внутренний в c ++? - PullRequest
2 голосов
/ 06 мая 2020

Я хочу поддерживать существующий API, который активно использует внутреннее перечисление, но мне нужно выделить перечисление в отдельный тип:

Это текущий API:

class Outer {
public:
    enum Inner {
        FIELD1
    };
};

Другими словами, пользователи в настоящее время ожидают установить Inner s, используя Outer::Inner. Я хотел бы сохранить имя, если это возможно, когда я создам новый внешний тип:

enum Inner {
    FIELD1
};

class Outer {
public:
    typedef Inner Inner;
}

Однако - это дает мне ошибку компилятора:

./Playground/file0.cpp:23:19: error: declaration of 'typedef enum Inner Outer::Inner' changes meaning of 'Inner' [-fpermissive]
   23 |     typedef Inner Inner;
      |                   ^~~~~
./Playground/file0.cpp:17:6: note: 'Inner' declared here as 'enum Inner'
   17 | enum Inner {
      |      ^~~~~

Если я просто изменю имя перечисления все работает нормально.

#include <cassert>

enum Enum {
    FIELD1
};

class Outer {
public:
    typedef Enum Inner;
};

int main() {

    Enum field = Enum::FIELD1;
    Outer::Inner field_alt = Outer::Inner::FIELD1;
    assert(field == field_alt);
    return 0;
}

Ответы [ 3 ]

6 голосов
/ 07 мая 2020

Ваш typedef для Inner должен ссылаться на Inner перечисление во внешней области , поэтому он должен выглядеть так:

typedef ::Inner Inner;

Я обычно предпочитаю using заявления, которые выглядят так:

using Inner = ::Inner;

Вот демонстрация

3 голосов
/ 07 мая 2020

Это запрещено из-за правила [basi c .scope.class] / 3:

Имя N, используемое в классе S, должно относиться к тому же самому объявление в своем контексте и при повторной оценке в завершенной области S. Диагностика c не требуется для нарушения этого правила.

Когда вы делаете typedef Inner Inner;, вы нарушаете это правило. Первый раз Inner появляется в этой строке, это использование имени, и обнаруживается, что оно относится к объявлению ::Inner. Но после того, как класс полностью определен, Inner теперь обращается к объявлению typedef. Итак, программа представляет собой плохо сформированный отчет о недоставке (G CC достаточно хорош, чтобы дать вам диагностику c).

Вам необходимо изменить его на:

typedef ::Inner Inner;

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

0 голосов
/ 07 мая 2020

Предоставленные решения ответили на мой первоначальный вопрос, но, к сожалению, не решили мои проблемы. В то время я не понимал, что API на самом деле использует преимущества внутренних перечислений.

enum Inner {
    FIELD1,
    FIELD2
};

class Base {
public:
    virtual void use(Inner val) = 0;
};

class Outer : public Base {
public:
    virtual void use(Inner val) override;
};

/*
 * Some API that can't change
 */
void someAPI(Outer &inst) {
    inst.use(Outer::FIELD1);
}

Другими словами, API, который мне нужно поддерживать, был не Outer::Inner::FIELD1, а Outer::FIELD1.

Как отметил @Brian, использование typedef для «подъема» внутреннего перечисления во внешнее не могло работать в текущем C ++.

В конце концов, я обнаружил, что могу поместить Inner в Base, а API может поддерживаться:

class Base {
public:
    enum Inner {
        FIELD1,
        FIELD2
    };

    virtual void use(Inner val) = 0;
};

class Outer : public Base {
public:
    virtual void use(Inner val) override;
};

/*
 * Some API that can't change
 */
void someAPI(Outer &inst) {
    inst.use(Outer::FIELD1);
}
...