У меня две классовые специализации. Я хочу, чтобы один из них использовался, когда существует T::A, а другой — когда существует T::B, что на практике должно быть взаимоисключающим. Я использую std::void_t< decltype( ... ) > для проверки существования. Я ожидаю, что это выражение не сможет оценить ни ту, ни другую специализацию, и поэтому я ожидаю, что SFINAE приведет к игнорированию одной из специализаций.
template< typename T, typename Enable = void >
class C{};
template< typename T >
class C< T, std::void_t< decltype( T::A ) > > {};
template< typename T >
class C< T, std::void_t< decltype( T::B ) > > {};
Однако MSVC просто дает мне
(On line containing T::B): error C2953: 'C<T,void>': class template has already been defined
(On line containing T::A): note: see declaration of 'C<T,void>'
Что я делаю не так?
Но в документации для std::void_t конкретно сказано, что это и есть его цель: en.cppreference.com/w/cpp/types/void_t
Но являются ли T::A
и T::B
взаимоисключающими в вашем реальном коде?
Ошибки компиляции возникают даже без попытки использовать класс для каких-либо фактических типов.
Clang также отвергает этот код, но не GCC. Уже не первый раз вижу проблемы с std::void_t
.
Я бы держался подальше от этого и предпочел бы decltype(void(T::A))
.
Или вы можете определить свой собственный надежный void_t
(код взят из cppreference):
template<typename... Ts> struct make_void { typedef void type; };
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
Замена void_t этой реализацией, похоже, решает проблему. Как вы думаете, это ошибка в компиляторе MSVC или в их реализации STL? Любая идея, если есть способ сообщить об этом?
gcc не выберет ни шаблон с std::void_t
, ни ваш void_t
. Он выберет шаблон по умолчанию.
@Swift-FridayPie Мне подходит с обоими void_t
вкусами. Возможно, вы определяете A
и B
как типы?
@JohnHaggerty Я считаю, что определение исправлено в стандарте, но это может быть ошибка компилятора. Вы можете попробовать подать его и посмотреть, что произойдет. Вы также можете сообщить об этом Clang. Обязательно сначала проверьте существующие отчеты.
@HolyBlackCat поцарапайте, кажется, void_t
механика не в ладах с видимостью T::A
, T::B
. Они должны быть public
. Это может быть связано с проблемой ОП.
@JohnHaggerty Под «исправлено в стандарте» я имею в виду, что для этого требуется конкретное определение (тот, который без вспомогательной структуры).
void_t
всегдаvoid
, поэтому нет никакой разницы между первым и этими двумя