Почему Visual Studio не позволяет мне использовать шаблонную функцию constexpr в enable if?
поэтому я свел это к минимальному, полному, проверяемому примеру, и кажется, что Visual Studio 2015 просто не позволит мне использовать шаблонный,
4 ответов
проблема заключается в том, что MSVC14/VS2015 не способен правильно разрешать выражения SFINAE в сочетании с возвращаемыми значениями функций constexpr в качестве параметров шаблона.
в качестве обходного пути вы можете назначить возвращаемое значение constexpr "статическому const" члену структуры и использовать этот член в качестве параметра шаблона.
#include <type_traits>
#include <iostream>
using std::enable_if_t;
using std::cout;
template <typename T>
constexpr bool condition() { return sizeof(T) > 1; }
template <typename T>
struct condition_ { static const bool value = condition<T>();};
template <typename T>
enable_if_t<condition_<T>::value> test() { cout << "true\n"; }
template <typename T>
enable_if_t<!condition_<T>::value> test() { cout << "false\n"; }
int main() {
test<int>();
test<bool>();
return 0;
}
http://rextester.com/VVNHB62598
Вы упомянули в комментариях, что ваша фактическая проблема появилась в другом случае, чем ваш MCVE (как инициализировать объект div_t?)
в этом случае решение может выглядеть так:
#include <type_traits>
#include <cstdlib>
#include <utility>
template <typename T>
using divtype = decltype(std::div(std::declval<T>(), std::declval<T>()));
template <typename T>
struct condition
{
static const bool value = divtype<T>{ 1, 0 }.quot != 0;
};
template <typename T>
std::enable_if_t<condition<T>::value, divtype<T>> make_div(const T quot, const T rem) { return{ quot, rem }; }
template <typename T>
std::enable_if_t<!condition<T>::value, divtype<T>> make_div(const T quot, const T rem) { return{ rem, quot }; }
int main() {
make_div<int>(1, 2);
return 0;
}
http://rextester.com/ULDFM22040
по этому запись блога команды Visual Studio C++ VS2015 не имеет (полной) поддержки для выражение SFINAE еще.
[1] Мы планируем начать реализация выражения SFINAE в компиляторе сразу после 2015 RTM, и мы планируем поставить его в обновлении до 2015 года, поддерживаемом для производственного использования. (Но не обязательно 2015 обновление 1. Это может занять больше времени.)
проблема в том, что вы в конечном итоге с двух разных шаблона test
вида:
template<class>void test()
и компилятор жалуется. Это может быть связано с ошибкой выражения SFINAE, где оно не оценивает выражение condition<T>()
"достаточно рано", или не по-другому.
вот решение:
template<std::size_t>
struct counter{ enum type{}; };
template<std::size_t N>
using counter_type=typename counter<N>::type;
template <typename T>
constexpr bool condition() { return sizeof(T) > 1; }
template <class T,counter_type<0>...,class=std::enable_if_t<condition<T>()>>
void test() { std::cout << "true\n"; }
template <class T,counter_type<1>...,class=std::enable_if_t<!condition<T>()>>
void test() { std::cout << "false\n"; }
теперь подписи шаблонов двух разных test
отличаются, а выражение SFINAE оценка condition<T>()
не работает совсем правильно не вызывает проблем.
еще один вариант ответа @Yakk:
template <class T, std::enable_if_t<(sizeof(T) > 1)>* = nullptr>
void test() { std::cout << "true\n"; }
template <class T, std::enable_if_t<!(sizeof(T) > 1)>* = nullptr>
void test() { std::cout << "false\n"; }
EDIT: просто увидел, что это в основном решение, на которое указал TC в комментариях.
EDIT2: исправленный код для компиляции в MSVC2015, см. комментарии.
Это известная проблема MSVC, и был упомянут в одном из своих сообщений в блоге. Происходит то, что компилятор не может распознать, что SFINAE делает вторую версию test()
отличается от первого, и ему нужна небольшая подсказка; достаточно простого фиктивного параметра, позволяющего дифференцировать две версии.
#include <type_traits>
#include <iostream>
using std::enable_if_t; using std::cout;
template <typename T>
constexpr bool condition() { return sizeof(T) > 1; }
#ifdef _MSC_VER
#define MSVC_DUMMY int /*msvc_dummy*/ = 0
#else // _MSC_VER
#define MSVC_DUMMY
#endif // _MSC_VER
template <typename T>
enable_if_t<condition<T>()> test() { cout << "true\n"; }
template <typename T>
enable_if_t<!condition<T>()> test(MSVC_DUMMY) { cout << "false\n"; }
int main() {
test<char>();
test<int>();
}
этой работает на MSVC, С минимальными изменениями кода. Его также легко удалить, как только они в конечном итоге заставят его работать без намека.
Если последовательный интерфейс, это может быть скрыто за вспомогательную функцию.
template <typename T>
enable_if_t<condition<T>()> test_() { cout << "true\n"; }
template <typename T>
enable_if_t<!condition<T>()> test_(MSVC_DUMMY) { cout << "false\n"; }
template <typename T>
auto test() { return test_<T>(); }