I recently encountered (at least I think this) erroneous behavior in MSVC ++ 2015 (oh, what news ...) when trying to compile the following code (clearly simplified):
#include <tuple> #include <functional> template<typename T> using Func = std::function<void(T)>; template<class...Ts> class Foo { public: using Tuples = std::tuple<Func<Ts>...>; Foo(Tuples arg) : m_tuples(arg) {} private: Tuples m_tuples; }; struct Bar {}; int main() { Foo<Bar> foo{std::make_tuple([](Bar) {})}; return 0; }
It works great with clang and gcc (with both new and older versions with C ++ 14 support), but in MSVC ++ 2015 it does not compile with a very interesting error message:
type_traits(1494): error C2893: Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...)' type_traits(1494): note: With the following template arguments: type_traits(1494): note: '_Callable=std::tuple<std::function<void (Bar)>> &' type_traits(1494): note: '_Types={Bar}'
The first strange behavior appears here: why does the compiler try to use a tuple as an object of a function? Further on the stack, it becomes clear: It mistakenly creates an instance of the element
template< class... UTypes > explicit constexpr tuple( UTypes&&... args );
instead of using the default value, which will demonstrate the correct behavior - it even seems that the additional SFINAE check in the MSVC STD implementation does not allow to catch this:
_Tuple_enable<tuple<tuple<std::function<void (Bar)>> &>, tuple<std::function<void (Bar)>>
This should obviously lead to the removal of this constructor from the overload set, since it just comes down to checking if tuple<tuple<std::function<void (Bar)>> &> converted to tuple<std::function<void (Bar)>> (AFAIK this conversion does not exist). I donโt understand why he didnโt just use the default copy constructor - or is my interpretation of the standard incorrect?
Since clang and gcc can both compile this code with the correct behavior, my question now is: Is this an error with Microsoft std implementation or a compiler error? For reference, changing the constructor to Foo(const Tuples& arg) forces MSVC to use the correct tuple constructor (and compiles the code perfectly), while both the alternatives Foo(Tuples&& arg) and Foo(Tuples& arg) also use the error constructor.
Full error log below:
1>------ Build started: Project: TestProject, Configuration: Debug Win32 ------ 1> test.cpp 1>g:\misc\microsoft visual studio 14.0\vc\include\type_traits(1494): error C2893: Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...)' 1> g:\misc\microsoft visual studio 14.0\vc\include\type_traits(1494): note: With the following template arguments: 1> g:\misc\microsoft visual studio 14.0\vc\include\type_traits(1494): note: '_Callable=std::tuple<std::function<void (Bar)>> &' 1> g:\misc\microsoft visual studio 14.0\vc\include\type_traits(1494): note: '_Types={Bar}' 1> g:\misc\microsoft visual studio 14.0\vc\include\functional(210): note: see reference to function template instantiation 'void std::_Invoke_ret<_Rx,_Callable&,_Ty>(std::_Forced<_Rx,true>,_Callable &,_Ty &&)' being compiled 1> with 1> [ 1> _Rx=void, 1> _Callable=_Decayed, 1> _Ty=Bar 1> ] 1> g:\misc\microsoft visual studio 14.0\vc\include\functional(208): note: while compiling class template member function 'void std::_Func_impl<_Decayed,_Alloc,_Ret,Bar>::_Do_call(Bar &&)' 1> with 1> [ 1> _Alloc=std::allocator<int>, 1> _Ret=void 1> ] 1> g:\misc\microsoft visual studio 14.0\vc\include\functional(136): note: see reference to class template instantiation 'std::_Func_impl<_Decayed,_Alloc,_Ret,Bar>' being compiled 1> with 1> [ 1> _Alloc=std::allocator<int>, 1> _Ret=void 1> ] 1> g:\misc\microsoft visual studio 14.0\vc\include\functional(339): note: see reference to class template instantiation 'std::_Is_large<_Myimpl>' being compiled 1> g:\misc\microsoft visual studio 14.0\vc\include\functional(318): note: see reference to function template instantiation 'void std::_Func_class<_Ret,Bar>::_Reset_alloc<_Ty,std::allocator<int>>(_Fx &&,const _Alloc &)' being compiled 1> with 1> [ 1> _Ret=void, 1> _Ty=std::tuple<std::function<void (Bar)>>, 1> _Fx=std::tuple<std::function<void (Bar)>>, 1> _Alloc=std::allocator<int> 1> ] 1> g:\misc\microsoft visual studio 14.0\vc\include\functional(318): note: see reference to function template instantiation 'void std::_Func_class<_Ret,Bar>::_Reset_alloc<_Ty,std::allocator<int>>(_Fx &&,const _Alloc &)' being compiled 1> with 1> [ 1> _Ret=void, 1> _Ty=std::tuple<std::function<void (Bar)>>, 1> _Fx=std::tuple<std::function<void (Bar)>>, 1> _Alloc=std::allocator<int> 1> ] 1> g:\misc\microsoft visual studio 14.0\vc\include\functional(484): note: see reference to function template instantiation 'void std::_Func_class<_Ret,Bar>::_Reset<std::tuple<std::function<void (Bar)>>>(_Fx &&)' being compiled 1> with 1> [ 1> _Ret=void, 1> _Fx=std::tuple<std::function<void (Bar)>> 1> ] 1> g:\misc\microsoft visual studio 14.0\vc\include\functional(484): note: see reference to function template instantiation 'void std::_Func_class<_Ret,Bar>::_Reset<std::tuple<std::function<void (Bar)>>>(_Fx &&)' being compiled 1> with 1> [ 1> _Ret=void, 1> _Fx=std::tuple<std::function<void (Bar)>> 1> ] 1> g:\misc\microsoft visual studio 14.0\vc\include\tuple(75): note: see reference to function template instantiation 'std::function<void (Bar)>::function<std::tuple<std::function<void (Bar)>>>(_Fx)' being compiled 1> with 1> [ 1> _Fx=std::tuple<std::function<void (Bar)>> 1> ] 1> g:\misc\microsoft visual studio 14.0\vc\include\tuple(75): note: see reference to function template instantiation 'std::function<void (Bar)>::function<std::tuple<std::function<void (Bar)>>>(_Fx)' being compiled 1> with 1> [ 1> _Fx=std::tuple<std::function<void (Bar)>> 1> ] 1> g:\misc\microsoft visual studio 14.0\vc\include\tuple(233): note: see reference to function template instantiation 'std::_Tuple_val<_This>::_Tuple_val<std::tuple<std::function<void (Bar)>>&>(_Other)' being compiled 1> with 1> [ 1> _This=std::function<void (Bar)>, 1> _Other=std::tuple<std::function<void (Bar)>> & 1> ] 1> g:\misc\microsoft visual studio 14.0\vc\include\tuple(233): note: see reference to function template instantiation 'std::_Tuple_val<_This>::_Tuple_val<std::tuple<std::function<void (Bar)>>&>(_Other)' being compiled 1> with 1> [ 1> _This=std::function<void (Bar)>, 1> _Other=std::tuple<std::function<void (Bar)>> & 1> ] 1> test.cpp(115): note: see reference to function template instantiation 'std::tuple<std::function<void (Bar)>>::tuple<std::tuple<std::function<void (Bar)>>&,,std::_Tuple_enable<std::tuple<>,std::tuple<>>::type>(_This2)' being compiled 1> with 1> [ 1> _This2=std::tuple<std::function<void (Bar)>> & 1> ] 1> test.cpp(115): note: see reference to function template instantiation 'std::tuple<std::function<void (Bar)>>::tuple<std::tuple<std::function<void (Bar)>>&,,std::_Tuple_enable<std::tuple<>,std::tuple<>>::type>(_This2)' being compiled 1> with 1> [ 1> _This2=std::tuple<std::function<void (Bar)>> & 1> ] 1> test.cpp(113): note: while compiling class template member function 'Foo<Bar>::Foo(std::tuple<std::function<void (Bar)>>)' 1> test.cpp(134): note: see reference to function template instantiation 'Foo<Bar>::Foo(std::tuple<std::function<void (Bar)>>)' being compiled 1> test.cpp(134): note: see reference to class template instantiation 'Foo<Bar>' being compiled ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========