C++ std::enable_if 回退?
C++ std::enable_if fallback?
我正在设置可变参数模板函数,以便能够在特定的 classes 系列上调用各种函数重载。到目前为止,当将不受支持的 class 传递给函数时,我已经能够 "break" 进行编译,但我希望能够提供有效的回退来处理 "unsupported" 运行时场景。
当前的实现是这样的:
struct ClassA {};
struct ClassB {};
struct ClassC {};
template<typename T> struct is_my_class : std::false_type {};
template<> struct is_my_class<ClassA> : std::true_type {};
template<> struct is_my_class<ClassB> : std::true_type {};
template<typename T>
constexpr bool is_my_class_v = is_my_class<T>::value;
void runOverload(ClassA c) { printf("ClassA overload\n"); }
void runOverload(ClassB c) { printf("ClassB overload\n"); }
template<typename T, typename = std::enable_if_t<is_my_class_v<T>>>
void run(T myClass)
{
runOverload(myClass);
};
template<typename T, typename... Ts>
void run(T myClass, Ts... classesLeft)
{
run(myClass);
run(classesLeft...);
};
int main()
{
ClassA a;
ClassB b;
ClassC c;
run(a, b); // works
run(c); // does not compile
}
在这里,我为run
做的两个最有希望(但仍然失败)的尝试:
1 - 在 is_my_class<T>
前面添加一个简单的 !
,出现以下错误:error C2995: 'void run(T)': function template has already been defined
template<typename T, typename = std::enable_if_t<!is_my_class_v<T>>>
void run(T myClass)
{
printf("Not supported\n");
};
2 - 制作更多 "primary" 模板定义,这会产生一个悲伤但明显的结果:error C2668: 'run': ambiguous call to overloaded function
template<typename T>
void run(T myClass)
{
printf("Not supported\n");
};
编辑
我忘了说明我正在寻找一个也兼容 C++11/14
的解决方案
您可以完全避免 enable_if
,只需使用编译时 if
来决定要执行的代码:
template<typename T>
void run(T myClass)
{
if constexpr (is_my_class_v<T>)
runOverload(myClass);
else
printf("Not supported\n");
}
这是一个demo。
有后备 runOverload
模板
template<typename T>
void runOverload(T myClass)
{
printf("Not supported\n");
};
尽管如果 c++17 可用,我会推荐@cigien 的解决方案,但我想补充一点,通过更改 enable_if
模板参数,因此改变了 "fallback function" 的签名。以下代码应该可以正常工作:
template<typename T, std::enable_if_t<!is_my_class_v<T>, int> = 0>
//template<typename T>
void run(T myClass) {
printf("error\n");
};
完整代码在 CompilerExplorer 上可用,已在 GCC 和 Clang trunk 上测试。
我还想补充一点,在我能想象到的所有用例中,最好有一个编译时错误(例如,您可以使用 static assertion
而不是 SFINAE
).
Here 您可以了解为什么即使使用两个 "ints" 作为模板参数,函数声明也不会模棱两可。
我写了下面的代码并且它有效。我认为你只是搞砸了 SFINAE 的语法。
#include <iostream>
#include <type_traits>
struct ClassA {};
struct ClassB {};
struct ClassC {};
template<typename T> struct is_my_class : std::false_type {};
template<> struct is_my_class<ClassA> : std::true_type {};
template<> struct is_my_class<ClassB> : std::true_type {};
template<typename T>
constexpr bool is_my_class_v = is_my_class<T>::value;
void runOverload(ClassA c) { printf("ClassA overload\n"); }
void runOverload(ClassB c) { printf("ClassB overload\n"); }
template<typename T, std::enable_if_t<is_my_class_v<T>,int> = 0>
void run(T myClass)
{
runOverload(myClass);
};
template<typename T, std::enable_if_t<not is_my_class_v<T>,int> = 0>
void run(T myClass)
{
printf("Not supported\n");
};
// wrote an extra SFINEA here to ensure that Ts aren't empty - else it might be an ODR issue despite the fact that it compiles
template<typename T, typename... Ts, std::enable_if_t<sizeof...(Ts) != 0,int> = 0>
void run(T myClass, Ts... classesLeft)
{
run(myClass);
run(classesLeft...);
};
int main()
{
ClassA a;
ClassB b;
ClassC c;
run(a, b);
run(c);
}
它打印
ClassA overload
ClassB overload
Not supported
我正在设置可变参数模板函数,以便能够在特定的 classes 系列上调用各种函数重载。到目前为止,当将不受支持的 class 传递给函数时,我已经能够 "break" 进行编译,但我希望能够提供有效的回退来处理 "unsupported" 运行时场景。
当前的实现是这样的:
struct ClassA {};
struct ClassB {};
struct ClassC {};
template<typename T> struct is_my_class : std::false_type {};
template<> struct is_my_class<ClassA> : std::true_type {};
template<> struct is_my_class<ClassB> : std::true_type {};
template<typename T>
constexpr bool is_my_class_v = is_my_class<T>::value;
void runOverload(ClassA c) { printf("ClassA overload\n"); }
void runOverload(ClassB c) { printf("ClassB overload\n"); }
template<typename T, typename = std::enable_if_t<is_my_class_v<T>>>
void run(T myClass)
{
runOverload(myClass);
};
template<typename T, typename... Ts>
void run(T myClass, Ts... classesLeft)
{
run(myClass);
run(classesLeft...);
};
int main()
{
ClassA a;
ClassB b;
ClassC c;
run(a, b); // works
run(c); // does not compile
}
在这里,我为run
做的两个最有希望(但仍然失败)的尝试:
1 - 在 is_my_class<T>
前面添加一个简单的 !
,出现以下错误:error C2995: 'void run(T)': function template has already been defined
template<typename T, typename = std::enable_if_t<!is_my_class_v<T>>>
void run(T myClass)
{
printf("Not supported\n");
};
2 - 制作更多 "primary" 模板定义,这会产生一个悲伤但明显的结果:error C2668: 'run': ambiguous call to overloaded function
template<typename T>
void run(T myClass)
{
printf("Not supported\n");
};
编辑 我忘了说明我正在寻找一个也兼容 C++11/14
的解决方案您可以完全避免 enable_if
,只需使用编译时 if
来决定要执行的代码:
template<typename T>
void run(T myClass)
{
if constexpr (is_my_class_v<T>)
runOverload(myClass);
else
printf("Not supported\n");
}
这是一个demo。
有后备 runOverload
模板
template<typename T>
void runOverload(T myClass)
{
printf("Not supported\n");
};
尽管如果 c++17 可用,我会推荐@cigien 的解决方案,但我想补充一点,通过更改 enable_if
模板参数,因此改变了 "fallback function" 的签名。以下代码应该可以正常工作:
template<typename T, std::enable_if_t<!is_my_class_v<T>, int> = 0>
//template<typename T>
void run(T myClass) {
printf("error\n");
};
完整代码在 CompilerExplorer 上可用,已在 GCC 和 Clang trunk 上测试。
我还想补充一点,在我能想象到的所有用例中,最好有一个编译时错误(例如,您可以使用 static assertion
而不是 SFINAE
).
Here 您可以了解为什么即使使用两个 "ints" 作为模板参数,函数声明也不会模棱两可。
我写了下面的代码并且它有效。我认为你只是搞砸了 SFINAE 的语法。
#include <iostream>
#include <type_traits>
struct ClassA {};
struct ClassB {};
struct ClassC {};
template<typename T> struct is_my_class : std::false_type {};
template<> struct is_my_class<ClassA> : std::true_type {};
template<> struct is_my_class<ClassB> : std::true_type {};
template<typename T>
constexpr bool is_my_class_v = is_my_class<T>::value;
void runOverload(ClassA c) { printf("ClassA overload\n"); }
void runOverload(ClassB c) { printf("ClassB overload\n"); }
template<typename T, std::enable_if_t<is_my_class_v<T>,int> = 0>
void run(T myClass)
{
runOverload(myClass);
};
template<typename T, std::enable_if_t<not is_my_class_v<T>,int> = 0>
void run(T myClass)
{
printf("Not supported\n");
};
// wrote an extra SFINEA here to ensure that Ts aren't empty - else it might be an ODR issue despite the fact that it compiles
template<typename T, typename... Ts, std::enable_if_t<sizeof...(Ts) != 0,int> = 0>
void run(T myClass, Ts... classesLeft)
{
run(myClass);
run(classesLeft...);
};
int main()
{
ClassA a;
ClassB b;
ClassC c;
run(a, b);
run(c);
}
它打印
ClassA overload
ClassB overload
Not supported