C++ 类型安全变量函数
我想写一个函数,它接受可变数量的字符串文字。如果我是用C写的,我会写一些类似的东西:C++ 类型安全变量函数,c++,c++11,templates,variadic-templates,c++17,C++,C++11,Templates,Variadic Templates,C++17,我想写一个函数,它接受可变数量的字符串文字。如果我是用C写的,我会写一些类似的东西: void foo(const char *first, ...); void foo(const char* args...) { for (const char* arg : args) { // Real work } } 然后这个电话看起来像: foo( "hello", "world", (const char*)NULL ); 感觉应该有可能在C++中做得更好。我
void foo(const char *first, ...);
void foo(const char* args...) {
for (const char* arg : args) {
// Real work
}
}
然后这个电话看起来像:
foo( "hello", "world", (const char*)NULL );
感觉应该有可能在C++中做得更好。我想到的最好的办法是:
template <typename... Args>
void foo(const char* first, Args... args) {
foo(first);
foo(args);
}
void foo(const char* first) { /* Do actual work */ }
但是我担心递归的本质,以及在我们得到一个参数之前不进行任何类型检查的事实,会使错误变得混乱,如果有人调用foo(“bad”,“argument”,“next”,42)
。我想写的是:
void foo(const char *first, ...);
void foo(const char* args...) {
for (const char* arg : args) {
// Real work
}
}
有什么建议吗
编辑:还有
void fn(std::initializer\u list args)
选项,但是这使得调用是foo({“hello”,“world”})代码>这是我想要避免的。我想你可能想要这样的东西:
template<class... Args,
std::enable_if_t<(std::is_same_v<const char*, Args> && ...), int> = 0>
void foo(Args... args ){
for (const char* arg : {args...}) {
std::cout << arg << "\n";
}
}
int main() {
foo("hello", "world");
}
模板
void foo(Args…Args){
for(const char*arg:{args…}){
std::对于C++17 Lilistate解决方案,不能+1
对于C++11解决方案,一种可能的方法是创建一个类型traits来生成多个值的“and”(类似于std::conjunction
,不幸的是,只有从C++17开始,当您可以使用折叠并且不再需要std::conjunction
时才可以使用它(谢谢))
使用C++14,multAnd()
可以编写为constepr
函数
template <bool ... Bs>
constexpr bool multAnd ()
{
using unused = bool[];
bool ret { true };
(void)unused { true, ret &= Bs ... };
return ret;
}
---编辑---
Jarod42(谢谢!)建议了一种更好的方法来开发multAnd
;比如
template <typename T, T ...>
struct int_sequence
{ };
template <bool ... Bs>
struct all_of : public std::is_same<int_sequence<bool, true, Bs...>,
int_sequence<bool, Bs..., true>>
{ };
模板
结构int_序列
{ };
模板
结构:public std::的所有元素都是相同的
{ };
从C++14开始,可以使用std::integer\u序列
而不是模仿(int\u序列
)。注意:不能只匹配字符串文本。最接近的方法是匹配常量字符
数组
要进行类型检查,请使用接受const char
数组的函数模板
要使用基于范围的for
循环它们,我们需要将其转换为初始值设定项列表
。我们可以直接使用基于范围的for
语句中的大括号来执行此操作,因为数组将衰减为指针
以下是函数模板的外观(注意:这适用于零个或多个字符串文字。如果需要一个或多个,请将函数签名更改为至少包含一个参数。):
模板
使用cstring_literal_type=const char(&)[N];
模板
void foo(cstring\u literal\u type…args)
{
for(常量字符*arg:{args…})
{
//实际工作
}
}
使用逗号运算符,只需执行以下操作:
#include <iostream>
#include <string>
#include <utility>
template<typename OneType>
void foo_(OneType&& one)
{
std::cout << one;
}
template<typename... ArgTypes>
void foo(ArgTypes&&... arguments)
{
(foo_(std::forward<ArgTypes>(arguments)), ...);
}
int main()
{
foo(42, 43., "Hello", std::string("Bla"));
}
namespace detail
{
void foo(std::initializer_list<const char*> strings);
}
template<typename... Types>
void foo(const Types... strings)
{
detail::foo({strings...});
}
请注意,这与C++11语法非常接近,可以实现完全相同的功能。请参见
#包括
自动功能=[](自动…cstrings){
静态断言((std::is_same_v&&…);
for(常量字符*字符串:{cstrings…}){
std::cout当所有其他答案都能解决问题时,您还可以执行以下操作:
#include <iostream>
#include <string>
#include <utility>
template<typename OneType>
void foo_(OneType&& one)
{
std::cout << one;
}
template<typename... ArgTypes>
void foo(ArgTypes&&... arguments)
{
(foo_(std::forward<ArgTypes>(arguments)), ...);
}
int main()
{
foo(42, 43., "Hello", std::string("Bla"));
}
namespace detail
{
void foo(std::initializer_list<const char*> strings);
}
template<typename... Types>
void foo(const Types... strings)
{
detail::foo({strings...});
}
此实现可编译为:
test.cpp: In instantiation of ‘void foo_1(const ArgTypes ...) [with ArgTypes = {const char*, int, const char*, const char*}]’:
test.cpp:17:29: required from here
test.cpp:12:16: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]
detail::foo({strings...});
~~~~~~~~~~~^~~~~~~~~~~~~~
而基于SFINAE的(liliscent的实施)产生:
test2.cpp: In function ‘int main()’:
test2.cpp:14:29: error: no matching function for call to ‘foo(const char [6], const char [6], int)’
foo("hello", "world", 42);
^
test2.cpp:7:6: note: candidate: ‘template<class ... Args, typename std::enable_if<(is_same_v<const char*, Args> && ...), int>::type <anonymous> > void foo(Args ...)’
void foo(Args... args ){
^~~
test2.cpp:7:6: note: template argument deduction/substitution failed:
test2.cpp:6:73: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
std::enable_if_t<(std::is_same_v<const char*, Args> && ...), int> = 0>
test2.cpp:在函数“int main()”中:
test2.cpp:14:29:错误:调用“foo(const char[6],const char[6],int)”时没有匹配的函数
傅(“你好”,“世界”,42);
^
test2.cpp:7:6:注意:候选者:“模板void foo(Args…)
void foo(Args…Args){
^~~
test2.cpp:7:6:注意:模板参数推断/替换失败:
test2.cpp:6:73:错误:“struct std::enable_if”中没有名为“type”的类型
std::如果\u t=0>
当然有可能,这会编译并运行您想要的(请注意)
#包括
模板
//呵呵,这是秘密
auto foo(const Char*…args)->decltype((Char const*)(*std::begin({args…})),(Char const*)(*std::end({args…})),void(0))
{
for(常量字符*arg:{args…}){
std::cout好吧,你能找到的最近的函数可以接受任意数量的const char*
,但是没有其他函数使用模板函数和转发:
void foo_impl(std::initializer_list<const char*> args)
{
...
}
template <class... ARGS>
auto foo(ARGS&&... args)
-> foo_impl({std::forward<ARGS>(args)...})
{
foo_impl({std::forward<ARGS>(args)...});
}
void foo\u impl(std::initializer\u list args)
{
...
}
模板
自动foo(ARGS&&…ARGS)
->foo_impl({std::forward(args)…})
{
foo_impl({std::forward(args)…});
}
微妙之处在于允许正常的隐式转换。现在……对于完全不同的东西
您可以编写一个类型包装结构,如下所示
template <typename, typename T>
struct wrp
{ using type = T; };
template <typename U, typename T>
using wrp_t = typename wrp<U, T>::type;
问题是你不能随心所欲地称呼它
foo("hello", "world");
因为编译器无法推断Args…
类型
显然,您可以显式地显示一个伪类型列表
foo<void, void>("hello", "world");
下面是一个完整的C++11工作示例
#include <iostream>
template <typename, typename T>
struct wrp
{ using type = T; };
template <typename U, typename T>
using wrp_t = typename wrp<U, T>::type;
template <typename ... Args>
void foo (wrp_t<Args, char const *> ... args)
{
for ( char const * arg : {args...} )
std::cout << "- " << arg << std::endl;
}
template <typename ... Args>
void bar (Args ... args)
{ foo<Args...>(args...); }
int main ()
{
bar("hello", "world"); // compile
// bar("hello", "world", 0); // compilation error
}
#包括
模板
结构wrp
{使用type=T;};
模板
使用wrp_t=typename wrp::type;
模板
void foo(wrp_t…args)
{
for(字符常量*arg:{args…})
std::cout您是否与调用语法foo(“hello”,“world”);
?如果没有,那么std::initializer\u list
是一个合适的参数类型。这不会触发?@gsamaras我想要的语法根本不起作用。如果t args…
意味着“std::initializer\u list
根据所有剩余参数初始化,然后是no.@Caleth-我更新了问题:我想避免({})(但感谢您澄清我的要求)Re:“担心[它]会使错误混淆”--试试看。投票表决。值得一提的是,这实际上是C++17中的std::conjunction
的一个实现。@lilistence-std::conjunction
!我记得有类似的东西,但我没有想到它的名字。谢谢。multAnd
可以简化
void foo_impl(std::initializer_list<const char*> args)
{
...
}
template <class... ARGS>
auto foo(ARGS&&... args)
-> foo_impl({std::forward<ARGS>(args)...})
{
foo_impl({std::forward<ARGS>(args)...});
}
template <typename, typename T>
struct wrp
{ using type = T; };
template <typename U, typename T>
using wrp_t = typename wrp<U, T>::type;
template <typename ... Args>
void foo (wrp_t<Args, char const *> ... args)
{
for ( char const * arg : {args...} )
std::cout << "- " << arg << std::endl;
}
foo("hello", "world");
foo<void, void>("hello", "world");
template <typename ... Args>
void bar (Args ... args)
{ foo<Args...>(args...); }
bar("hello", "world");
#include <iostream>
template <typename, typename T>
struct wrp
{ using type = T; };
template <typename U, typename T>
using wrp_t = typename wrp<U, T>::type;
template <typename ... Args>
void foo (wrp_t<Args, char const *> ... args)
{
for ( char const * arg : {args...} )
std::cout << "- " << arg << std::endl;
}
template <typename ... Args>
void bar (Args ... args)
{ foo<Args...>(args...); }
int main ()
{
bar("hello", "world"); // compile
// bar("hello", "world", 0); // compilation error
}