Does the standard require std::tuple_size to be SFINAE-friendly?

1.1k views Asked by At

Edit append: The question title was "do Visual Studio compiler or Clang have incorrect behavior"- but that have been changed.

So I add here that clang and gcc compiles it the way I intended, but VS does not.

I have the following code:

template<typename S, typename T, std::size_t... I>
  void
  print_tuple_like(S& s, const T& t, std::index_sequence<I...>)
  {
    void* unused[] = { &(s << std::get<I>(t))... };      
  }

template<typename S, typename T,
         std::size_t N = std::tuple_size<decltype(T::children)>::value>
    S& operator<<(S& s, const T& t)
{
    print_tuple_like(s, t.children, std::make_index_sequence<N>{});
    return s;
}

and I get an compiler error:

1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\utility(313): error C2338: The C++ Standard doesn't define tuple_size for this type.
1>  c:\users\jonas\documents\visual studio 2015\projects\consoleapplication7\consoleapplication7\consoleapplication7.cpp(36): note: see reference to class template instantiation 'std::tuple_size<unknown-type>' being compiled
1>  c:\users\jonas\documents\visual studio 2015\projects\consoleapplication7\consoleapplication7\consoleapplication7.cpp(43): note: see reference to function template instantiation 'void print_tuple_like<S,std::tuple<Signature::A,Signature::B>,0,1>(S &,const T &,std::integer_sequence<_Ty,0,1>)' being compiled
1>          with
1>          [
1>              S=std::ostream,
1>              T=std::tuple<Signature::A,Signature::B>,
1>              _Ty=size_t
1>          ]
1>  c:\users\jonas\documents\visual studio 2015\projects\consoleapplication7\consoleapplication7\consoleapplication7.cpp(50): note: see reference to function template instantiation 'S &operator <<<std::ostream,Signature,2>(S &,const T &)' being compiled
1>          with
1>          [
1>              S=std::ostream,
1>              T=Signature
1>          ]

That is because the following code in visual studio:

// TEMPLATE STRUCT tuple_size 
template<class _Tuple>  
struct tuple_size   {   // size of non-tuple
    static_assert(_Always_false<_Tuple>::value, "The C++ Standard doesn't define tuple_size for this type.");   
};

make the substitution failure into a hard failure- making the SFINAE SFIAE

If I remove

static_assert(_Always_false<_Tuple>::value, "The C++ Standard doesn't define tuple_size for this type.");

it works.

Is the code strecthing the c++ standard rules? Or is Microsoft in the wrong?

3

There are 3 answers

0
Yakk - Adam Nevraumont On BEST ANSWER

The standard mandates that tuple_size not be SFINAE friendly, but this is considered by many to be a defect, and looks on-track to be fixed in C++17.

It is required that all specializations (which in standard-speak for "template instantiations") of tuple_size basically be std::integral_constant<size_t, ?> or inherit from it. (It leaves some freedom to the compiler)

If the primary template isn't defined, then it doesn't violate that. But if the primary template is defined to be an empty struct (or similar), then that empty struct is a template instance (which the standard calls a "specialization") that is not essentially std::integral_constant<size_t, ?>.

By my reading, it would be legal for the primary template (the "failure" case) to be std::integral_constant<size_t, 42> or any other constant. Useless and evil, but legal. But being an empty struct violates the standard.

However, the arguments about changing this to mandate an empty struct are about wording, and not about if it is a good idea.

Thanks to @T.C. who solved this in a comment thread on another answer here.

14
Ryan Haining On

N4140 20.4.1 describes tuple_size's non-specialized version as:

template <class T> class tuple_size; // undefined

By providing a definition, the MSVC lib has violated the standard.

You should realize however, that your question is not about the compilers involved, but rather the standard library implementations.

0
Hans vesselgård On

Could something like this perhaps work with some modifications?

template<typename S, typename T,    typename   tuple_element<0,decltype(T::children)>::type >
    S& operator<<(S& s, const T& t)