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++中做得更好。我

我想写一个函数,它接受可变数量的字符串文字。如果我是用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
 }