Это mcve моего кода: (если это имеет значение, Options_proxy
и Options
имеют constexpr ctors). Я знаю, что это еще далеко не из простых, но не может упростить его больше пока еще экспонирование ошибки:Неполный тип в функции друга
template <class Impl>
struct Options_proxy : Impl {
using Flag = typename Impl::Flag;
friend constexpr auto operator!(Flag f) -> Options_proxy {
return {}; // <-- error here
};
};
template <class Impl>
struct Options : Impl {
using Flag = typename Impl::Flag;
};
struct File_options_impl {
enum class Flag : unsigned { nullflag, read, write };
friend constexpr auto operator!(Flag f) -> Options_proxy<File_options_impl>;
};
using File_options = Options<File_options_impl>;
auto foo()
{
!File_options::Flag::write; // <-- required from here
}
НКУ 6 и 7 дают эту ошибку:
In instantiation of 'constexpr Options_proxy<File_options_impl> operator!(Options_proxy<File_options_impl>::Flag)':
required from ... etc etc...
error: return type 'struct Options_proxy<File_options_impl>' is incomplete
лязг компилироваться OK.
код соответствует в НКУ, если:
- я удалить
constexpr
изoperator!
или
- добавить объект типа
Options_proxy<File_options_impl>
перед вызовом оператора:
так:
auto foo()
{
Options_proxy<File_options_impl> o;
!File_options::Flag::write; // <-- now OK in gcc also
}
Является ли это ошибка или НКИ некоторое Неопределенное поведение в коде, что-то вроде неопределенного или нет диагностики требуется?
Что касается мотивации написания такого кода:
Я хочу создать (для развлечения в основном) типа безопасной системы флагов/опций (без макросов):
Библиотеки черной магия:
template <class Impl>
requires Options_impl<Impl>
struct Options : Impl {
// go crazy
};
код пользователя:
struct File_options_impl {
// create a system where here the code
// needs to be as minimal as possible to avoid repetition and user errors
// this is what the user actually cares about
enum class Flag : unsigned { nullflag = 0, read = 1, write = 2, create = 4};
// would like not to need to write this,
// but can't find a way around it
friend constexpr auto operator!(Flag f1) -> Options_proxy<File_options_impl>;
friend constexpr auto operator+(Flag f1, Flag f2) -> Options_proxy<File_options_impl>;
friend constexpr auto operator+(Flag f1, Options_proxy<File_options_impl> f2) -> Options_proxy<File_options_impl>;
};
using File_options = Options<File_options_impl>;
, а затем:
auto open(File_options opts);
using F_opt = File_options::Flag;
open(F_opt::write + !F_opt::create);
Благодарим вас за ответ. 1. У отдельной специализации будут разные члены 'Flag'. Они могут иметь тот же самый флаг, если 'Impl :: Flag' является псевдонимом. Это не произойдет в текущей системе. 2. Как я уже сказал, это mcve, поэтому реальный код намного сложнее. Я согласен, что это немного злоупотребляет объявлением друга, но я не вижу другого способа определить операторы для 'Flag' вне' File_options_impl' (в библиотечной части кода). 3. Я не уверен, что это невозможно. Я был бы очень разочарован, если бы это было невозможно. – bolov