Matt Matt - 2 months ago 12
C++ Question

MSVC 2013 'type' : is not a member of 'std::enable_if<false,void>

My SFINAE code using

std::enable_if
compiles in GCC & Clang, but not in MSVC 2013.

The code (also available on cpp.sh) is

#include <iostream>
#include <type_traits>

template <typename T, typename ... AdditionalInputs>
typename std::enable_if<sizeof...(AdditionalInputs) == 0, void>::type
CallDoDataProcessing(T var) {
std::cout << sizeof...(AdditionalInputs) << " additional inputs" << std::endl;
}

template <typename T, typename ... AdditionalInputs>
typename std::enable_if<sizeof...(AdditionalInputs) == 1, void>::type
CallDoDataProcessing(T var) {
std::cout << sizeof...(AdditionalInputs) << " additional inputs" << std::endl;
}

int main() {
CallDoDataProcessing<int>(3);
CallDoDataProcessing<int, int>(3);
return 0;
}


In GCC/Clang, this works perfectly, in MSVC though, I get:

Error 1 error C2039: 'type' : is not a member of 'std::enable_if<false,void>' c:\Users\mrussell\documents\visual studio 2013\Projects\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.cpp 5 1 ConsoleApplication1


The compiled and run output should be:

0 additional inputs
1 additional inputs


I've seen some similar issues on SO, but none had a clear answer or were slightly tangental.

Reading the MSVC enable_if page, this should work...

How can I use SFINAE in MSVC2013?

UPDATE

Just as a note, this does work in the positive case. For instance, if I comment out the first function, and the call to it, then the rest compiles. i.e. the
enable_if<true, void>
on
CallDoDataProcessing
does have a
type
member.

However, commenting out the second function and call to it (so, leaving the version where
sizeof...(AdditionalInputs) == 0
does not work though. Same error.

This suggests that the
sizeof...(AdditionalInputs) == 0
call is not being matched, but I can't figure out why it wouldn't be.

Answer

Try tag dispatching.

template<std::size_t>
struct size {};

namespace details {
  template <typename T, typename ... AdditionalInputs>
  void CallDoDataProcessing(T var, size<0>) {
    std::cout << sizeof...(AdditionalInputs) << ", aka 0, additional inputs" << std::endl;
  }

  template <typename T, typename ... AdditionalInputs, std::size_t N>
  void CallDoDataProcessing(T var, size<N>) {
    std::cout << sizeof...(AdditionalInputs) << " additional inputs" << std::endl;
  }
}
template <typename T, typename ... AdditionalInputs>
void CallDoDataProcessing(T var) {
  details::CallDoDataProcessing<T, AdditionalInputs>( var, size<sizeof...(AdditionalInputs)>{} );
}

SFINAE is really badly supported by MSVC. Your code looks like valid SFINAE. The fact that MSVC fails to do the right thing is not surprising.

MSVC works much much better with tag dispatching in my experience, and I find it even results in easier to understand code and even error messages sometimes.

What it does not permit is noticing "no, you cannot do this" before the body of the calling function, to make the calling function also state "no, I cannot be done".

Comments