Nano Hash - криптовалюты, майнинг, программирование

SFINAE здесь не применяется?

Я писал что-то, чтобы использовать SFINAE, чтобы не генерировать функцию при определенных условиях. Когда я использую метакод напрямую, он работает так, как ожидалось, но когда я использую код косвенно через другой класс, он не работает должным образом.

Я думал, что это было связано с VC++, но похоже, что g++ также имеет это, поэтому мне интересно, есть ли какая-то причина, по которой SFINAE не применяется к этому случаю.

Код прост. Если используемый класс не является базовым классом «класса коллекции», не создавайте функцию.

#include <algorithm>
#include <type_traits>

#define USE_DIRECT 0
#define ENABLE 1

class A{};
class B{};
class C{};
class D{};
class collection1 : A, B, C {};
class collection2 : D {};

#if USE_DIRECT
template<typename X>
typename std::enable_if<std::is_base_of<X, collection1>::value, X>::type fn(X x)
{
    return X();
}

# if ENABLE
template<typename X>
typename std::enable_if<std::is_base_of<X, collection2>::value, X>::type fn(X x)
{
    return X();
}
# endif

#else // USE_DIRECT

template<typename X, typename COLLECTION>
struct enable_if_is_base_of
{
    static const int value = std::is_base_of<X, COLLECTION>::value;
    typedef typename std::enable_if<value, X>::type type;
};

template<typename X>
typename enable_if_is_base_of<X, collection1>::type fn(X x)
{
    return X();
}

# if ENABLE
template<typename X>
typename enable_if_is_base_of<X, collection2>::type fn(X x)
{
    return X();
}
# endif
#endif // USE_DIRECT

int main()
{
    fn(A());
    fn(B());
    fn(C());
    fn(D());

   return 0;
}

Если я установлю для USE_DIRECT значение 1, а для ENABLE значение 0, компиляция не удастся, так как нет функции fn, которая принимает параметр D. Установка ENABLE в 1 предотвратит появление этой ошибки.

Однако, если я установлю USE_DIRECT на 0 и ENABLE на 0, произойдет сбой с другими сообщениями об ошибках, но в том же случае нет fn, который принимает параметр D. Однако установка ENABLE в 1 приведет к сбою всех 4 вызовов функций.

Для вашего удобства вот код в онлайн-компиляторе: http://goo.gl/CQcXHr

Может кто-нибудь объяснить, что здесь происходит и почему?

Похоже, это может быть связано с шаблонами псевдонимов, используемыми в SFINAE приводит к серьезной ошибке, но и на это никто не ответил.

Для справки, вот ошибки, которые были сгенерированы g++:

main.cpp: In instantiation of 'struct enable_if_is_base_of<A, collection2>':                                                                   
main.cpp:45:53:   required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection2>::type fn(X) [with X = A]'       
main.cpp:54:8:   required from here                                                                                                            
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, A>'                                                               
  typedef typename std::enable_if<std::is_base_of<X, COLLECTION>::value, X>::type type;                                                        
                                                                                  ^                                                            
main.cpp: In instantiation of 'struct enable_if_is_base_of<B, collection2>':                                                                   
main.cpp:45:53:   required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection2>::type fn(X) [with X = B]'       
main.cpp:55:8:   required from here                                                                                                            
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, B>'                                                               
main.cpp: In instantiation of 'struct enable_if_is_base_of<C, collection2>':                                                                   
main.cpp:45:53:   required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection2>::type fn(X) [with X = C]'       
main.cpp:56:8:   required from here                                                                                                            
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, C>'                                                               
main.cpp: In instantiation of 'struct enable_if_is_base_of<D, collection1>':                                                                   
main.cpp:38:53:   required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection1>::type fn(X) [with X = D]'       
main.cpp:57:8:   required from here                                                                                                            
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, D>'                                                               

  • Э-э, в вашем косвенном случае, как следует из названия, ошибка не в прямом контексте, так что это настоящая ошибка... 28.12.2014

Ответы:


1

Только замена, происходящая в непосредственном контексте, может привести к ошибке вывода:

§14.8.2 [temp.deduct]/p8

Только недопустимые типы и выражения в непосредственном контексте типа функции и ее типов параметров шаблона могут привести к ошибке вывода. [ Примечание. Вычисление замененных типов и выражений может привести к побочным эффектам, таким как создание экземпляров специализаций шаблонов классов и/или специализаций шаблонов функций, создание неявно- определенные функции и т. д. Такие побочные эффекты не относятся к «непосредственному контексту» и могут привести к неправильному построению программы.конец примечания]

Для подписи fn требуется полное объявление специализации enable_if_is_base:

§14.7.1 [temp.inst]/p1

Если специализация шаблона класса не была создана явно (14.7.2) или явно специализирована (14.7.3), специализация шаблона класса создается неявно, когда на специализацию ссылаются в контексте, который требует полностью определенного типа объекта, или когда полнота типа класса влияет на семантику программы.

Компилятору не удается сгенерировать специализацию:

template<typename X, typename COLLECTION>
struct enable_if_is_base_of
{
    static const int value = std::is_base_of<X, COLLECTION>::value;
    typedef typename std::enable_if<value, X>::type type;
};

потому что замена typename std::enable_if<value, X>::type приводит к отсутствующему типу, если value оценивается как false, что не находится в непосредственном контексте.

28.12.2014
  • Я почти уверен, что это ответ, который я ищу. У меня просто проблемы с пониманием. о.О 29.12.2014
  • Теперь с помощью @BenVoigt я понимаю, что это значит. Однако, поскольку он ссылается на стандарт, я отмечу ваш ответ. 29.12.2014

  • 2

    Как уже было сказано, ваша схема SFINAE не может работать. Однако вы можете использовать следующее не очень красивое решение для достижения того, чего вы, вероятно, хотите:

    #include <algorithm>
    #include <type_traits>
    #include <iostream>
    
    class A{};
    class B{};
    class C{};
    class D{};
    class collection1 : A, B, C {};
    class collection2 : D {};
    
    template<typename X, class Enable = void>
    struct enable_if_is_base_of;
    
    template<typename X>
    struct enable_if_is_base_of<X, typename std::enable_if<std::is_base_of<X, collection1>::value>::type> {
      static X fn(X x) {
        (void) x;
        std::cout << "collection1" << std::endl;
        return X();
      }
    };
    
    template<typename X>
    struct enable_if_is_base_of<X, typename std::enable_if<std::is_base_of<X, collection2>::value>::type> {
      static X fn(X x) {
        (void) x;
        std::cout << "collection2" << std::endl;
        return X();
      }
    };
    
    int main() {
      enable_if_is_base_of<A>::fn(A());
      enable_if_is_base_of<B>::fn(B());
      enable_if_is_base_of<C>::fn(C());
      enable_if_is_base_of<D>::fn(D());
    }
    

    РЕАЛЬНАЯ ДЕМО

    28.12.2014
  • Да, это действительно неправильно. Я пытался упростить чтение кода. Это на самом деле затрудняет чтение и делает функцию функцией-членом, а не чистой. Хотя это интересно. 29.12.2014

  • 3

    Проблема в том, что если тип не существует, typedef не пропускается, это ошибка.

    Решение: нет определения типа

    template<typename X, typename COLLECTION>
    struct enable_if_is_base_of : std::enable_if<std::is_base_of<X, COLLECTION>::value, X>
    {};
    

    Теперь элемент type существует (посредством наследования) тогда и только тогда, когда он существует в экземпляре enable_if.

    28.12.2014
  • Мне нравится это решение. Это очень просто. Я просто не понимаю, почему тип не существует, это проблема. Я думал, что в этом весь смысл. Если нет типа, то подстановка не удалась. Неудачная замена не является ошибкой, так что двигайтесь дальше. 29.12.2014
  • О, я понял. std::is_base_of<X, COLLECTION>::value оценивается как ничего, что приводит к неправильному формату typedef type, что приводит к тому, что класс не существует. Поскольку он не существует, он не может иметь члена с именем type. Его наследование делает код хорошо сформированным в обоих контекстах (независимо от того, является ли он базовым или нет), поэтому затем можно применить SFINAE. 29.12.2014
  • @ Адриан: Не совсем. is_base_of — это либо true_type, либо false_type, и оба определяют value. Что хорошо, так как SFINAE также не встречается в списке базовых классов. В конечном итоге вы наследуете от std::enable_if<false>, в котором нет члена с именем type, именно так, как вам нужно. SFINAE работает только для удаления функций шаблона из списка кандидатов во время разрешения перегрузки. Это не относится к определениям типов, спискам базовых классов и т. д. 29.12.2014
  • Именно это имеет в виду §14.8.2 [temp.deduct]/p8. Верно? 29.12.2014
  • @ Адриан: Да, это другой способ сказать то же самое. 29.12.2014
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..