Почему 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>(); }