C++ 可变参数模板方法特化
C++ Variadic template method specialization
我有一些可变模板方法,看起来像这样:
template<typename ... Args>
void Invoke(const char* funcName, Args ... args) const;
template<typename ... Args>
void Invoke(const char* funcName, Args ... args) const
{
SPrimitive params[] = { args ... };
SomeOtherInvoke(funcName, params, sizeof ... (Args));
}
这里是 SPrimitive - 只是一个简单的结构,带有任何原始类型的构造函数。
我想为某个复杂类型再定义一个Invoke。这是我的问题:
是否可以在 C++ 11/14 中进行可变参数模板方法专业化?
我的意思是这样的(为简单起见,我的类型将是 int):
template<int ... Args>
void Invoke(const char* funcName, Args ... args)
{
int params[] = { args ... };
SomeComplexInvoke(funcName, params, sizeof ... (Args));
}
这里我想要一个特殊化,它接受任何 int 类型的参数计数,所以我可以这样调用它:
Invoke("method", 2, 4 ,9);
正如@Jarod42 提到的,它不应该通过专业化来完成。在您的示例中,如果所有参数类型都是 int
,您需要一些特殊的东西,所以让我们编写一个模板来检查它:
template<typename ref, typename t, typename ...types>
struct all_same {
static constexpr bool value = std::is_same<ref, t>::value && all_same<ref, types...>::value;
};
template<typename ref, typename t>
struct all_same<ref, t> {
static constexpr bool value = std::is_same<ref, t>::value;
};
它检查第一个类型参数是否等于所有其他类型参数。然后在 Invoke
中我们应该 select params
type based on args...
types:
template<typename ... Args>
void Invoke(const char* funcName, Args ... args)
{
using params_type = typename std::conditional<all_same<int, Args...>::value, int, SPrimitive>::type;
params_type params[] = { args ... };
SomeOtherInvoke(funcName, params, sizeof ... (Args));
}
现在为了演示让我们定义:
struct SPrimitive{
};
void SomeOtherInvoke(const char*, SPrimitive*, size_t) {
std::cout << "Invoked for SPrimitive\n";
}
void SomeOtherInvoke(const char*, int*, size_t) {
std::cout << "Invoked for int\n";
}
并致电
Invoke("foo", SPrimitive());
Invoke("foo", SPrimitive(), SPrimitive());
Invoke("foo", 1, 2, 3, 4);
输出为:
Invoked for SPrimitive
Invoked for SPrimitive
Invoked for int
这就是你要的。
我也遇到了这个问题,找到了更简单的方法。这个想法是只使用一个简单的 static_cast.
template <class ...args
void test (args ...a)
{
test___( sizeof... (a), static_cast<int*>(&a)... );
}
extern void test___(int count,...);
注:
我故意使用 c 省略号来表明您可以简单地将 cpp 可变参数传递给 c 省略号参数。这个cpp是通用的吗?我不知道。
同时让您知道使用两全其美是多么方便。 Cpp 可变参数函数编写起来很笨重。 C 省略号不识别类型,这真的很重要。
最后请注意我们如何在可变参数上使用运算符的地址。这个cpp是通用的吗?我不知道。
我想以 c 省略号笨拙而 cpp 与 c 结合拯救了我的案例结束。
//This function expects RECT pointers
void addRect (int count,...);
int main ()
{
RECT r = {0,0,640,480};
addRect (3,r,r,r}; // However it is so easy to forget that
// program will crash
}
因此,将 public 方法编写为可变参数函数,该函数又将正确的内容传递给省略号版本。
注意:一切都可以通过 cpp variadics 完成,但它很笨重,而且很多时候看起来很糟糕。
我已经给出了备选答案。现在这是另一个。请注意,我仍然相信第一个回答的人;他的回答作为智力演示很有用,可能有其时间和地点。
注意:我的替代方案接受任何可转换为 int 的值。而他的建议只接受int,除非你使用std::remove_reference,否则不会接受int&。比较复杂。
此替代方案基于模板重载。如果我们想要一个只接受整数或可转换为整数的对象的函数;我们只需添加 int 作为可变参数模板函数的第一个参数!
template <class ...args>
void example (int i,args ...rest)
{
example__(sizeof... (rest)+1, i, rest...);
}
void example (int count,...);
注意:再一次,一切都可以用 Cpp Variadics 完成。但我认为 C 省略号确实使任务更简单,编译错误更少,如果您记得只将基元和指针传递给 C 省略号,则运行时错误的可能性很小。
如果您希望您的函数在一次函数调用中接受多种类型,请执行以下操作。我将省略私有实现函数和 public 实现。
template <class ...args>
void example (int i,args &...rest);
template <class ...args>
void example (const std::string&, args &...rest);
最后,如果您希望您的函数接受各种类型,但在单独的函数调用中,我建议使用命名约定。
template <class ...args>
void intExample (int i,args &...rest);
template <class ...args>
void stringExample (const std::string&, args &...rest);
template <class ...args>
int getInt (int i,args &...rest);
template <class ...args>
std::string getString (const std::string&, args &...rest);
注意:通过保留最后一个示例中的第一个参数,您可以确保至少有一个参数必须传递给您的函数。
好了,今天就到这里。如果你做到了这一步,你就太棒了。感谢您的收看。如果你喜欢我的视频;单击该订阅按钮。粉碎那个按钮。
编辑_
我在 Cpp Variadics 中发现了一个美,我想在这里展示。如您所知,Cpp 模板是漂亮的 C 宏,类型安全且符合命名空间。
struct ColumnStyle
{
int type;
int align;
int width;
int min_width;
int max_width;
Int margin;
};
我想扩展这个 class,但我希望它仍然作为一个聚合 class。
struct LogColumn : ColumnStyle
{
template <class ...args>
LogColumn (const wchar_t *caption, HBITMAP icon, args ...style)
: ColumnStyle {style...}, // amazing! I can simply pass the variadic arguments to the aggregate ctor!
header_caption (caption),
header_icon (icon)
{}
const wchart_t *header_caption;
HBITMAP header_icon;
};
我有一些可变模板方法,看起来像这样:
template<typename ... Args>
void Invoke(const char* funcName, Args ... args) const;
template<typename ... Args>
void Invoke(const char* funcName, Args ... args) const
{
SPrimitive params[] = { args ... };
SomeOtherInvoke(funcName, params, sizeof ... (Args));
}
这里是 SPrimitive - 只是一个简单的结构,带有任何原始类型的构造函数。
我想为某个复杂类型再定义一个Invoke。这是我的问题: 是否可以在 C++ 11/14 中进行可变参数模板方法专业化? 我的意思是这样的(为简单起见,我的类型将是 int):
template<int ... Args>
void Invoke(const char* funcName, Args ... args)
{
int params[] = { args ... };
SomeComplexInvoke(funcName, params, sizeof ... (Args));
}
这里我想要一个特殊化,它接受任何 int 类型的参数计数,所以我可以这样调用它:
Invoke("method", 2, 4 ,9);
正如@Jarod42 提到的,它不应该通过专业化来完成。在您的示例中,如果所有参数类型都是 int
,您需要一些特殊的东西,所以让我们编写一个模板来检查它:
template<typename ref, typename t, typename ...types>
struct all_same {
static constexpr bool value = std::is_same<ref, t>::value && all_same<ref, types...>::value;
};
template<typename ref, typename t>
struct all_same<ref, t> {
static constexpr bool value = std::is_same<ref, t>::value;
};
它检查第一个类型参数是否等于所有其他类型参数。然后在 Invoke
中我们应该 select params
type based on args...
types:
template<typename ... Args>
void Invoke(const char* funcName, Args ... args)
{
using params_type = typename std::conditional<all_same<int, Args...>::value, int, SPrimitive>::type;
params_type params[] = { args ... };
SomeOtherInvoke(funcName, params, sizeof ... (Args));
}
现在为了演示让我们定义:
struct SPrimitive{
};
void SomeOtherInvoke(const char*, SPrimitive*, size_t) {
std::cout << "Invoked for SPrimitive\n";
}
void SomeOtherInvoke(const char*, int*, size_t) {
std::cout << "Invoked for int\n";
}
并致电
Invoke("foo", SPrimitive());
Invoke("foo", SPrimitive(), SPrimitive());
Invoke("foo", 1, 2, 3, 4);
输出为:
Invoked for SPrimitive
Invoked for SPrimitive
Invoked for int
这就是你要的。
我也遇到了这个问题,找到了更简单的方法。这个想法是只使用一个简单的 static_cast.
template <class ...args
void test (args ...a)
{
test___( sizeof... (a), static_cast<int*>(&a)... );
}
extern void test___(int count,...);
注:
我故意使用 c 省略号来表明您可以简单地将 cpp 可变参数传递给 c 省略号参数。这个cpp是通用的吗?我不知道。
同时让您知道使用两全其美是多么方便。 Cpp 可变参数函数编写起来很笨重。 C 省略号不识别类型,这真的很重要。
最后请注意我们如何在可变参数上使用运算符的地址。这个cpp是通用的吗?我不知道。
我想以 c 省略号笨拙而 cpp 与 c 结合拯救了我的案例结束。
//This function expects RECT pointers
void addRect (int count,...);
int main ()
{
RECT r = {0,0,640,480};
addRect (3,r,r,r}; // However it is so easy to forget that
// program will crash
}
因此,将 public 方法编写为可变参数函数,该函数又将正确的内容传递给省略号版本。
注意:一切都可以通过 cpp variadics 完成,但它很笨重,而且很多时候看起来很糟糕。
我已经给出了备选答案。现在这是另一个。请注意,我仍然相信第一个回答的人;他的回答作为智力演示很有用,可能有其时间和地点。
注意:我的替代方案接受任何可转换为 int 的值。而他的建议只接受int,除非你使用std::remove_reference,否则不会接受int&。比较复杂。
此替代方案基于模板重载。如果我们想要一个只接受整数或可转换为整数的对象的函数;我们只需添加 int 作为可变参数模板函数的第一个参数!
template <class ...args>
void example (int i,args ...rest)
{
example__(sizeof... (rest)+1, i, rest...);
}
void example (int count,...);
注意:再一次,一切都可以用 Cpp Variadics 完成。但我认为 C 省略号确实使任务更简单,编译错误更少,如果您记得只将基元和指针传递给 C 省略号,则运行时错误的可能性很小。
如果您希望您的函数在一次函数调用中接受多种类型,请执行以下操作。我将省略私有实现函数和 public 实现。
template <class ...args>
void example (int i,args &...rest);
template <class ...args>
void example (const std::string&, args &...rest);
最后,如果您希望您的函数接受各种类型,但在单独的函数调用中,我建议使用命名约定。
template <class ...args>
void intExample (int i,args &...rest);
template <class ...args>
void stringExample (const std::string&, args &...rest);
template <class ...args>
int getInt (int i,args &...rest);
template <class ...args>
std::string getString (const std::string&, args &...rest);
注意:通过保留最后一个示例中的第一个参数,您可以确保至少有一个参数必须传递给您的函数。
好了,今天就到这里。如果你做到了这一步,你就太棒了。感谢您的收看。如果你喜欢我的视频;单击该订阅按钮。粉碎那个按钮。
编辑_ 我在 Cpp Variadics 中发现了一个美,我想在这里展示。如您所知,Cpp 模板是漂亮的 C 宏,类型安全且符合命名空间。
struct ColumnStyle
{
int type;
int align;
int width;
int min_width;
int max_width;
Int margin;
};
我想扩展这个 class,但我希望它仍然作为一个聚合 class。
struct LogColumn : ColumnStyle
{
template <class ...args>
LogColumn (const wchar_t *caption, HBITMAP icon, args ...style)
: ColumnStyle {style...}, // amazing! I can simply pass the variadic arguments to the aggregate ctor!
header_caption (caption),
header_icon (icon)
{}
const wchart_t *header_caption;
HBITMAP header_icon;
};