Проблема с размером пакета параметров в enable_if - PullRequest
0 голосов
/ 24 января 2020

Следующий пример дает сбой во всех основных компиляторах: clang, gcc и visual studio.

Я хотел бы знать, что с ним не так, похоже, он очень прост:

если sizeof...(TYPES) == 2, то он должен исключить одну перегрузку и принять другую, если 0, 1 или больше 2, то он должен принять первую перегрузку и исключить 2-ю.

Почему не не так ли это работает?

#include <iostream>
#include <type_traits>

template <typename... TYPES>
struct Test {
    template <std::enable_if_t<(sizeof...(TYPES) != 2), int> = 0>
    Test() {
        std::cout << "A\n";
    }

    template <std::enable_if_t<(sizeof...(TYPES) == 2), int> = 0>
    Test() {
        std::cout << "B\n";
    }
};

int
main() {

    Test<int, int> t1;
    Test<int, int, int> t2;

    return 0;
}

clang ошибка:

In file included from C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:1:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\iostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\istream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\ostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\ios:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xlocnum:12:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\cmath:505:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xtgmath.h:13:
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xtr1common:54:40: error: no type named 'type' in 'std::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration
using enable_if_t = typename enable_if<_Test, _Ty>::type;
                                       ^~~~~
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:6:17: note: in instantiation of template type alias 'enable_if_t' requested here
        template <std::enable_if_t<(sizeof...(TYPES) != 2), int> = 0>
                       ^
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:20:17: note: in instantiation of template class 'Test<int, int>' requested here
        Test<int, int> t1;
                       ^
In file included from C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:1:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\iostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\istream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\ostream:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\ios:11:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xlocnum:12:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\cmath:505:
In file included from C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xtgmath.h:13:
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\include\xtr1common:54:40: error: no type named 'type' in 'std::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration
using enable_if_t = typename enable_if<_Test, _Ty>::type;
                                       ^~~~~
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:11:17: note: in instantiation of template type alias 'enable_if_t' requested here
        template <std::enable_if_t<(sizeof...(TYPES) == 2), int> = 0>
                       ^
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:21:22: note: in instantiation of template class 'Test<int, int, int>' requested here
        Test<int, int, int> t2;
                            ^
2 errors generated.
[Finished in 0.8s]

gcc ошибка:

C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp: In instantiation of 'struct Test<int, int>':
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:20:17:   required from here
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:7:2: error: no type named 'type' in 'struct std::enable_if<false, int>'
  Test() {
  ^~~~
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp: In instantiation of 'struct Test<int, int, int>':
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:21:22:   required from here
C:\Users\joaopires\Dropbox\++A\tests\par_pack.cpp:12:2: error: no type named 'type' in 'struct std::enable_if<false, int>'
  Test() {
  ^~~~
[Finished in 2.1s]

visual studio ошибка:

1>C:\Users\joaopires\Dropbox\++A\A++\A++\main.cpp(160,1): error C2938: 'std::enable_if_t<false,int>' : Failed to specialize alias template
1>C:\Users\joaopires\Dropbox\++A\A++\A++\main.cpp(159): message : see reference to alias template instantiation 'std::enable_if_t<false,int>' being compiled
1>C:\Users\joaopires\Dropbox\++A\A++\A++\main.cpp(208): message : see reference to class template instantiation 'Test<int,int,int>' being compiled
1>Done building project "A++.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Ответы [ 2 ]

4 голосов
/ 24 января 2020

Это не работает, так как ваши функции-члены шаблона SFINAE в параметре шаблона из класса. SFINAE работает только с типами и выражениями в непосредственном контексте, здесь это означает параметр шаблона, который принадлежит сигнатуре шаблона конструкторов.

Чтобы обойти это, мы можем ввести новый параметр шаблона в конструкторе и присвоить ему значение по умолчанию. значение, которое зависит от шаблона класса.

#include <iostream>
#include <type_traits>

template <typename... TYPES>
struct Test {
    template <std::size_t i = sizeof...(TYPES), std::enable_if_t<i != 2, int> = 0>
    Test() {
        std::cout << "A\n";
    }

    template <std::size_t i = sizeof...(TYPES), std::enable_if_t<i == 2, int> = 0>
    Test() {
        std::cout << "B\n";
    }
};

int
main() {

    Test<int, int> t1;
    Test<int, int, int> t2;

    return 0;
}
1 голос
/ 24 января 2020

Начиная с C ++ 20, вы можете использовать requires для удаления нежелательного конструктора:

template <typename... TYPES>
struct Test {
    Test() requires(sizeof...(TYPES) != 2) { std::cout << "A\n"; }
    Test() requires(sizeof...(TYPES) == 2) { std::cout << "B\n"; }
};

Даже если в этом случае простая if (constexpr) сделает работу:

template <typename... TYPES>
struct Test {
    Test()
    {
        if constexpr (sizeof...(TYPES) != 2) {
            std::cout << "A\n";
        } else {
            std::cout << "B\n";
        }
    }
};
...