Я пытаюсь упростить код шаблона, определяя некоторую информацию о классе как статические члены constexpr. Вот очень упрощенный пример:
template <typename _Tp>
class Test
{
public:
static constexpr bool is_array = std::is_array_v<_Tp>;
template <typename U = _Tp, typename std::enable_if_t<!is_array>>* dummy = nullptr>
void print()
{
std::cout << "It's not an array\n";
}
template <typename U = _Tp, typename std::enable_if_t<is_array>>* dummy = nullptr>
void print()
{
std::cout << "It's an array\n";
}
};
int main()
{
Test<char[]>().print();
Test<char>().print();
}
Clang говорит
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:2385:44: error: no type named 'type' in 'std::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration
using enable_if_t = typename enable_if<_Cond, _Tp>::type;
^~~~~
test.cc:11:49: note: in instantiation of template type alias 'enable_if_t' requested here
template <typename U = _Tp, typename = std::enable_if_t<!is_array>>
^
test.cc:26:5: note: in instantiation of template class 'Test<char []>' requested here
Test<char[]>().print();
Он работает, если я полностью квалифицирую is_array, как в Test<U>::is_array, но у меня есть несколько параметров шаблона, и, похоже, если я сделаю это таким образом, это сделает его еще более беспорядочным.
Есть ли способ делать то, что мне нужно, кроме макроса?
@bipll Да, в этом случае это сработает. Мне пришлось бы объединить кучу выбранных SFINAE методов в большие, с if constexpr. Однако он компилируется и работает. Я искал способ просто заставить компилятор выбрать правильный метод.





Если вы хотите, чтобы SFINAE работала для метода класса, у вас должен быть тест, основанный на определенном параметре шаблона метода (например, U, в вашем случае).
Если вы отметите is_array, если он зависит от _Tp (параметр шаблона класса), а не от U.
Но, если вы можете использовать хотя бы C++ 14, у вас могут быть переменные шаблона, поэтому вы можете использовать шаблоны is_array.
template <typename U>
static constexpr bool is_array = std::is_array_v<U>;
Это немного упростит
Вы также можете удалить typename перед std::enable_if_t (спасибо _t) и отменить dummy (который не используется).
Ниже приведен полный пример компиляции.
#include <iostream>
#include <type_traits>
template <typename T>
struct Test
{
template <typename U>
static constexpr bool is_array = std::is_array_v<U>;
template <typename U = T,
std::enable_if_t<not is_array<U>>* = nullptr>
void print()
{ std::cout << "It's not an array\n"; }
template <typename U = T,
std::enable_if_t<is_array<U>>* = nullptr>
void print()
{ std::cout << "It's an array\n"; }
};
int main()
{
Test<char[]>().print();
Test<char>().print();
}
Альтернативой (возможно, лучше) является использование параметра шаблона bool вместо U; Например
template <typename T>
struct Test
{
static constexpr bool is_array = std::is_array_v<T>;
template <bool B = is_array,
std::enable_if_t<not B>* = nullptr>
void print()
{ std::cout << "It's not an array\n"; }
template <bool B = is_array,
std::enable_if_t<B>* = nullptr>
void print()
{ std::cout << "It's an array\n"; }
};
Это работает, и не так уж и далеко. У меня есть несколько bool, которые я хотел бы использовать, и я надеялся избежать их всех. Если бы я сделал это с помощью шаблона constexpr, мне пришлось бы передавать несколько типов, которые все равно будут беспорядочными, но я я полагаю, можно было бы сократить более сложные выражения.
Как насчет одного
print()с ветвямиif constexprвнутри?