Ограничение компилятора MSVC C++ — черта не работает в CRTP

У меня есть проблема с MSVC 2017 и 2019, связанная с этим ограничением: я хотел бы знать во время компиляции, имеет ли определенный «производный» класс шаблона CRTP определенную функцию.

Я получил это как оценку черты класса:

template <typename T>
class function_exists_trait
{
private:
    template <typename U>
    static auto test_todo(U * u) -> decltype(&U::exists) {}
    static auto test_todo(...) -> std::false_type {}

public:
    static constexpr bool value{ !std::is_same<decltype(test_todo(static_cast<T*>(nullptr))), std::false_type>::value };
};

Я протестировал его с некоторыми тривиальными классами:

struct t1 { void exists() {} };
struct t2 { void exists() {} };
struct t3 {};
struct t4 : public t1 {};
struct t5 : public t3 {};
struct t6 : public t3 { void exists() {} };

Я получил ожидаемые результаты. Как и ожидалось, оценки дают: 1 1 0 1 0 1 с этим тестом: cout << function_exists_trait<t1>::value << " " << ...

Я получил ожидаемые результаты для следующих простых реализаций CRTP (0 1 1 1):

template <typename t> struct u1 {};
struct crtp1 : public u1<crtp1> { void exists() {} };
template <typename t> struct u2 { void exists() {} };
struct crtp2 : public u2<crtp2> {};

cout << function_exists_trait<u1<int>>::value << " "
     << function_exists_trait<crtp1>::value << " "
     << function_exists_trait<u2<int>>::value << " "
     << function_exists_trait<crtp2>::value << endl;

Проблема вот в чем: при попытке оценить трейт внутри базового класса CRTP ничего не работает и я не понимаю почему.

template <typename t> struct u3 { 
    static inline constexpr bool value{ function_exists_trait<t>::value }; 
};
struct crtp3 : public u3<crtp3> { void exists() {} };

template <typename t> struct u4 { 
    void exists() {}
    static inline constexpr bool value{ function_exists_trait<t>::value };
};
struct crtp4 : public u4<crtp4> {};

template <typename t> struct u5 {
    void exists() {}
    static inline constexpr bool value{ function_exists_trait<t>::value };
};
struct crtp5 : public u5<crtp5> {
    void exists() {}
};

Следующий код дает этот результат: 0 0 - 0 0 - 0 0 - 0 0

cout << function_exists_trait<u3<int>>::value << " " << u3<int>::value << " - "
        << function_exists_trait<crtp3>::value << " " << crtp3::value << " - " 
        << function_exists_trait<crtp4>::value << " " << crtp4::value << " - "
        << function_exists_trait<crtp5>::value << " " << crtp5::value << endl;

Я опубликовал эту проблему, думая, что делаю что-то не так, но, похоже, это проблема с MSVC. Благодаря П.В. который показал мне, что это ограничение MSVC. Он показал мне, что точно такой же код дает такой результат в gcc: 0 0 - 1 1 - 1 1 - 1 1

У кого-нибудь есть предложения, как решить эту проблему. Возможно, есть очевидное решение с MSVC для запуска ожидаемого результата. Или просто другой способ достичь той же цели под MSVC.


person Aesope    schedule 21.05.2019    source источник
comment
Пожалуйста, опубликуйте минимально воспроизводимый пример в своем вопросе, чтобы мы могли вам помочь.   -  person Richard Critten    schedule 21.05.2019
comment
Извините, достаточно ли следующей ссылки в качестве минимального, полного и проверяемого примера ( stackoverflow.com/questions/56231762/ ). Текущий пост является продолжением связанного. Подскажите, достаточно ли. Спасибо!   -  person Aesope    schedule 22.05.2019
comment
Вопросы @Aesope по SO должны быть автономными, то есть содержать достаточно информации, чтобы на них можно было ответить самостоятельно, не требуя доступа к другому вопросу. Можно ссылаться на существующие вопросы, если их не обязательно читать, чтобы решить этот вопрос.   -  person Dale K    schedule 22.05.2019
comment
Я понимаю. Спасибо за советы! :)   -  person Aesope    schedule 22.05.2019
comment
Этот ответ объясняет, что происходит, а также предоставляет обходной путь (используйте функцию). Похоже, MSVC еще не внедрил новые правила C++17. Предположительно, вы ориентируетесь на С++ 17 (поскольку вы используете встроенные переменные), но, вероятно, стоит отметить, что поведение MSVC корректно в С++ 14. Из-за этого изменения между стандартными версиями я думаю, что использование функции вместо члена данных более безопасно и более переносимо.   -  person bogdan    schedule 23.05.2019
comment
Большое спасибо богдан. Ваша ссылка хорошо объясняет проблему и обходной путь. Кроме того, я хотел бы знать, что вы имеете в виду, говоря, что использование функции вместо члена данных является более безопасным и переносимым?   -  person Aesope    schedule 23.05.2019
comment
@Aesope Я имел в виду, что он будет вести себя одинаково в разных компиляторах и версиях. Для статических элементов данных, учитывая только последние версии компилятора, у нас есть Clang, который реализует правильное (различное) поведение C++ 14 и C++ 17 в зависимости от установленного вами стандартного режима, затем есть GCC, который, кажется, все это время реализовывали поведение C++17, а MSVC и EDG, которые, похоже, до сих пор реализовывали только поведение C++14, даже в режиме C++17. С другой стороны, создание экземпляра функции-члена работает одинаково во всех четырех из них, и так уже давно.   -  person bogdan    schedule 23.05.2019
comment
Чтобы уточнить, я не имел в виду это как общее утверждение о членах данных и функциях; Я не поклонник общих утверждений, таких как сделать все функцией или что-то в этом роде :-). Я имел в виду конкретно этот случай статических элементов данных, у которых есть инициализатор, указанный в определении класса, используемый в контексте, где точка создания экземпляра имеет решающее значение.   -  person bogdan    schedule 23.05.2019
comment
@bogdan Я понимаю твои мысли. Кроме того, спасибо, что указали и нашли время, чтобы объяснить различные реализации компиляторов. Их трудно понять с точки зрения новичка.   -  person Aesope    schedule 24.05.2019


Ответы (1)


Как обнаружил богдан, это рабочая альтернатива:

template <typename t> struct u6 {
    void exists() {}
    static constexpr bool value() { return function_exists_trait<t>::value; }
};
struct crtp6 : public u6<crtp6> {
    void exists() {}
};

Спасибо всем!

person Aesope    schedule 23.05.2019