enable_if+可变模板:是UB还是MSVC错误

enable_if + variadic templates: is it UB or a MSVC bug?

本文关键字:UB 还是 MSVC 错误 if+ enable      更新时间:2023-10-16

GCC 4.9.2、clang 3.5.0和MSVC 19(x86)按照预期编译以下内容:

#include <iostream>
#include <tuple>
#include <type_traits>
using namespace std;
template<size_t I = 0, typename... Tp>
inline typename enable_if<I == sizeof...(Tp), void>::type
    tuple_for_each(tuple<Tp...> &)
{
    cout << sizeof...(Tp) << endl;
}
template<size_t I = 0, typename... Tp>
inline typename enable_if<I < sizeof...(Tp), void>::type
    tuple_for_each(tuple<Tp...> & t)
{
    tuple_for_each<I + 1, Tp...>(t);
}
int main()
{
    auto t = make_tuple(1, 2.3);
    tuple_for_each(t);
}

MSVC 18(x64)报告以下内容:

Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.
main.cpp
main.cpp(18) : error C2770: invalid explicit template argument(s) for 'enable_if<I<0x01,void>::type tuple_for_each(std::tuple<_Types1...> &)'
        main.cpp(16) : see declaration of 'tuple_for_each'
        main.cpp(24) : see reference to function template instantiation 'void tuple_for_each<0x00,int,double>(std::tuple<int,double> &)' being compiled
main.cpp(18) : error C2893: Failed to specialize function template 'enable_if<I==0x01,void>::type tuple_for_each(std::tuple<_Types1...> &)'
        With the following template arguments:
        'I=0x01'
        'Tp={int, double}'

是UB在某个地方,还是只是一个编译器错误?(如果是后者,有什么解决办法吗?)

找到了一个解决方法:

template<size_t I = 0, typename... Tp,
         typename = typename enable_if<I == sizeof...(Tp), void>::type, int = 0>
inline void tuple_for_each(tuple<Tp...> &)
{
    cout << sizeof...(Tp) << endl;
}
template<size_t I = 0, typename... Tp,
         typename = typename enable_if<I != sizeof...(Tp), void>::type>
inline void tuple_for_each(tuple<Tp...> & t)
{
    tuple_for_each<I + 1, Tp...>(t);
}

注意I != sizeof...(Tp)中的!=。试试<,你会看到:

Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.
main.cpp
main.cpp(27) : fatal error C1004: unexpected end-of-file found

它本身就很壮观。(GCC和Clang都使用<,但这次不使用MSVC19。)


尝试使用

template <bool B, typename T> using enable_alias = typename enable_if<B, T>::type;

最初的变体最终进入ICE:

Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.
main.cpp
C:Program Files (x86)Microsoft Visual Studio 12.0VCINCLUDExlocnum(1105) : fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'f:ddvctoolscompilercxxfeslp1coutdname.c', line 4155)
 To work around this problem, try simplifying or changing the program near the locations listed above.
Please choose the Technical Support command on the Visual C++ Help menu, or open the Technical Support help file for more information