Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Won't Visual Studio let me use a Templatized, constexpr Function in enable_if?

So I've boiled this down to the minimal, complete, verifiable example and it seems that Visual Studio 2015 just won't allow me to use a templatized, constexpr function in an enable_if.

For example:

template <typename T>
constexpr bool condition() { return sizeof(T) > 1; }

Gives me the error:

error C2995: enable_if<_Test,T>::type test(void): function template has already been defined

When I try to use it in substitution failure is not an error compilation like this:

template <typename T>
enable_if_t<condition<T>()> test() { cout << "true\n"; }

template <typename T>
enable_if_t<!condition<T>()> test() { cout << "false\n"; }

This works fine in gcc: http://ideone.com/m9LDdS
And it works fine in Visual Studio 2015 if I remove the templatization of condition. I believe that constexpr functions were introduced in c++11, why isn't Visual Studio 2015 supporting this? Is it a bug?

like image 503
Jonathan Mee Avatar asked Jan 11 '17 14:01

Jonathan Mee


2 Answers

The problem seems to be that MSVC14/VS2015 is not capable of correctly resolving SFINAE expressions in combination with return values of constexpr functions as template parameters.

As a workaround you can assign the return value of your constexpr to a 'static const' member of a struct and use this member as template parameter.

#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


You also mentioned in the comments that your actual problem appeared in another case than your MCVE (How can I Initialize a div_t Object?)

For this case the workaround might look like this:

#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


According to this Visual Studio C++ Team blog entry VS2015 does not have (complete) support for Expression SFINAE yet.

[1] We’re planning to start implementing Expression SFINAE in the compiler immediately after 2015 RTM, and we’re planning to deliver it in an Update to 2015, supported for production use. (But not necessarily 2015 Update 1. It might take longer.)

like image 119
Simon Kraemer Avatar answered Nov 12 '22 07:11

Simon Kraemer


The problem is that you end up with two different template test of the form:

template<class>void test()

and the compiler complains. This may be related to expression SFINAE failure, where it doesn't evaluate the expression of condition<T>() "early enough", or fails in another way.

Here is a workaround:

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"; }

Now the template signatures of the two different test differ, and the expression SFINAE evaluation of condition<T>() not working quite right doesn't cause a problem.

like image 25
Yakk - Adam Nevraumont Avatar answered Nov 12 '22 05:11

Yakk - Adam Nevraumont