有没有一种好方法可以在 C++ 的可变参数模板中对函数参数实施类型限制?
Is there a good way to enforce type restrictions on function parameters in a variadic template in C++?
我有一个枚举,我们称它为 Type。它具有这样的值:
enum Type { STRING, TYPE_A_INT, TYPE_B_INT};
我想编写一个函数 Foo,它可以接受任意多个 {int, string} 类型的值,但强制模板参数与参数类型匹配。
理想情况下它的行为如下:
Foo<STRING, TYPE_A_INT>("str", 32); // works
Foo<STRING, TYPE_B_INT>("str", 32); // works
Foo<STRING, TYPE_B_INT, TYPE_A_INT, STRING>("str", 32, 28, "str"); // works
Foo<STRING, TYPE_B_INT>("str", "str"); // doesn't compile
有办法吗?
似乎我可以做类似下面的事情,但这行不通,因为 Args 是 Type 而 args 是 {string, int}。
template<typename Arg, typename... Args>
std::enable_if<(std::is_same<Arg, STRING>::value)>
Foo(String arg, Args... args) {
// Do stuff to arg, then make recursive call.
Foo(args);
}
template<typename Arg, typename... Args>
std::enable_if<(std::is_same<Arg, TYPE_A_INT>::value)>
Foo(int arg, Args... args) {
// Do stuff to arg, then make recursive call.
Foo(args);
}
我可以将参数包装成类似
的形式
pair<Type, string>
pair<Type, int>
但最好避免这种情况。
一个简单的方法是创建一个从枚举器到所需类型的映射,并使用它来构建函数参数列表——您可以将其视为 "enumerator traits",我猜:
#include <iostream>
#include <string>
enum Type {STRING, TYPE_A_INT, TYPE_B_INT};
template<Type> struct type_from;
template<> struct type_from<STRING> { using type = std::string; };
template<> struct type_from<TYPE_A_INT> { using type = int; };
template<> struct type_from<TYPE_B_INT> { using type = int; };
template<Type E> using type_from_t = typename type_from<E>::type;
template<Type... Es> void Foo(type_from_t<Es>... args)
{
// Do stuff with args.
using expander = int[];
(void)expander{0, (std::cout << args << ' ', 0)...};
std::cout << '\n';
}
int main()
{
Foo<STRING, TYPE_A_INT>("str", 32); // works
Foo<STRING, TYPE_B_INT>("str", 32); // works
Foo<STRING, TYPE_B_INT, TYPE_A_INT, STRING>("str", 32, 28, "str"); // works
// Foo<STRING, TYPE_B_INT>("str", "str"); // doesn't work
}
如果您取消注释最后一行,您将收到一条很好的错误消息,告诉您究竟是什么参数导致了问题。
当然,这并不能确保函数参数类型与枚举器特征给出的类型完全相同,而是确保它们中的每一个都存在有效的隐式转换。据我了解,这就是您想要的,因为您的示例将字符串文字传递给 std::string
s.
我有一个枚举,我们称它为 Type。它具有这样的值:
enum Type { STRING, TYPE_A_INT, TYPE_B_INT};
我想编写一个函数 Foo,它可以接受任意多个 {int, string} 类型的值,但强制模板参数与参数类型匹配。
理想情况下它的行为如下:
Foo<STRING, TYPE_A_INT>("str", 32); // works
Foo<STRING, TYPE_B_INT>("str", 32); // works
Foo<STRING, TYPE_B_INT, TYPE_A_INT, STRING>("str", 32, 28, "str"); // works
Foo<STRING, TYPE_B_INT>("str", "str"); // doesn't compile
有办法吗?
似乎我可以做类似下面的事情,但这行不通,因为 Args 是 Type 而 args 是 {string, int}。
template<typename Arg, typename... Args>
std::enable_if<(std::is_same<Arg, STRING>::value)>
Foo(String arg, Args... args) {
// Do stuff to arg, then make recursive call.
Foo(args);
}
template<typename Arg, typename... Args>
std::enable_if<(std::is_same<Arg, TYPE_A_INT>::value)>
Foo(int arg, Args... args) {
// Do stuff to arg, then make recursive call.
Foo(args);
}
我可以将参数包装成类似
的形式pair<Type, string>
pair<Type, int>
但最好避免这种情况。
一个简单的方法是创建一个从枚举器到所需类型的映射,并使用它来构建函数参数列表——您可以将其视为 "enumerator traits",我猜:
#include <iostream>
#include <string>
enum Type {STRING, TYPE_A_INT, TYPE_B_INT};
template<Type> struct type_from;
template<> struct type_from<STRING> { using type = std::string; };
template<> struct type_from<TYPE_A_INT> { using type = int; };
template<> struct type_from<TYPE_B_INT> { using type = int; };
template<Type E> using type_from_t = typename type_from<E>::type;
template<Type... Es> void Foo(type_from_t<Es>... args)
{
// Do stuff with args.
using expander = int[];
(void)expander{0, (std::cout << args << ' ', 0)...};
std::cout << '\n';
}
int main()
{
Foo<STRING, TYPE_A_INT>("str", 32); // works
Foo<STRING, TYPE_B_INT>("str", 32); // works
Foo<STRING, TYPE_B_INT, TYPE_A_INT, STRING>("str", 32, 28, "str"); // works
// Foo<STRING, TYPE_B_INT>("str", "str"); // doesn't work
}
如果您取消注释最后一行,您将收到一条很好的错误消息,告诉您究竟是什么参数导致了问题。
当然,这并不能确保函数参数类型与枚举器特征给出的类型完全相同,而是确保它们中的每一个都存在有效的隐式转换。据我了解,这就是您想要的,因为您的示例将字符串文字传递给 std::string
s.