Использование std :: visit в классе, наследующем от std :: variant - libstdc++ vs libС++

Рассмотрим следующий фрагмент кода:

struct v : std::variant<int, std::vector<v>> { };

int main()
{
    std::visit([](auto){ }, v{0});
}
  • clang++ 7 с -stdlib=libc++ -std=c++2a компилирует код;

  • g++ 9 с -std=c++2a не удается скомпилировать код со следующей ошибкой:

    /opt/compiler-explorer/gcc-trunk-20180711/include/c++/9.0.0/variant:94:29: ошибка: неполный тип 'std :: variant_size', используемый в вложенном указателе имени

     inline constexpr size_t variant_size_v = variant_size<_Variant>::value;
    
                             ^~~~~~~~~~~~~~
    

живой пример на godbolt.org


  • Соответствуют ли обе реализации Стандарту?

  • Если нет, то какая реализация верна здесь и почему?

Ответ 1

[variant.visit] в С++ 17 не использует variant_size_v, но он работает в текущем рабочем черновике в результате редакционного изменения. Я не вижу никаких указаний на то, что LWG рассмотрел это изменение до того, как оно вступило, но с тех пор он несколько раз смотрел на эту часть стандарта и еще не возражал против этого, поэтому я буду утверждать, что он находится в необходимый факт.

Между тем, проблема 305 LWG, которая была передана LEWG, явно потребовала бы std::variant. Когда эта проблема решена - так или иначе - она также должна решить эту проблему.

Ответ 2

Похоже, что это ошибка в gcc-реализации. Согласно cppreference, это называется, как будто призывая invoke на std::get. std::get<> определяется для всего, что конвертируется в std::variant (поскольку он принимает аргумент std::variant посредством ссылки пересылки). Ваша структура конвертируется в std::variant, и поэтому std::get сам работает над вашей структурой в gcc.

Тот факт, что реализация gcc решила использовать std::variant_size качестве части его реализации visit является их детальность реализации, а тот факт, что она не работает (и не должна) для вашей структуры, не имеет значения.

Вывод: это ошибка в gcc из-за надзора в реализации.