MSVC 2013 'type' : is not a member of '

2019-06-17 03:46发布

问题:

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.

回答1:

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".