首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Typesafe函数

Typesafe函数
EN

Stack Overflow用户
提问于 2018-05-15 15:04:11
回答 9查看 2.1K关注 0票数 16

我想要编写一个函数,它接受一个可变数目的字符串文本。如果我是用C写的话,我就得写这样的东西:

代码语言:javascript
复制
void foo(const char *first, ...);

然后电话就会看起来像:

代码语言:javascript
复制
foo( "hello", "world", (const char*)NULL );

感觉应该有可能在C++上做得更好。我想出的最好办法是:

代码语言:javascript
复制
template <typename... Args>
void foo(const char* first, Args... args) {
    foo(first);
    foo(args);
}

void foo(const char* first) { /* Do actual work */ }

称为:

代码语言:javascript
复制
foo("hello", "world");

但是,我担心递归的性质,以及在到达单个参数之前不进行任何类型检查的事实,如果有人调用foo("bad", "argument", "next", 42),就会造成错误混淆。我想写的是:

代码语言:javascript
复制
void foo(const char* args...) {
    for (const char* arg : args) {
        // Real work
    }
}

有什么建议吗?

编辑:还有void fn(std::initializer_list<const char *> args)的选项,但这使得调用成为foo({"hello", "world"});,这是我想要避免的。

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2018-05-15 20:15:14

虽然所有其他答案都解决了这个问题,但您还可以执行以下操作:

代码语言:javascript
复制
namespace detail
{
    void foo(std::initializer_list<const char*> strings);
}

template<typename... Types>
void foo(const Types... strings)
{
    detail::foo({strings...});
}

这种方法(至少对我来说)比使用SFINAE和使用C++11更易读,而且,它允许您将foo的实现移动到cpp文件中,这可能也很有用。

编辑:至少在GCC 8.1中,当使用非const char*参数调用时,我的方法似乎产生了更好的错误信息:

代码语言:javascript
复制
foo("a", "b", 42, "c");

这一实现的编译方式如下:

代码语言:javascript
复制
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(丁香的实施)则产生:

代码语言:javascript
复制
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>
票数 4
EN

Stack Overflow用户

发布于 2018-05-15 15:11:57

我想你可能想要这样的东西:

代码语言:javascript
复制
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");
}
票数 15
EN

Stack Overflow用户

发布于 2018-05-15 17:18:00

注意:不可能只匹配字符串文本。最接近的是匹配一个const char数组。

若要进行类型检查,请使用接受const char数组的函数模板。

要用基于范围的for循环它们,我们需要将其转换为initializer_list<const char*>。我们可以在基于范围的for语句中直接使用大括号,因为数组将衰减为指针。

下面是函数模板的样子(注意:这适用于零或多个字符串文本)。如果需要一个或多个参数,请将函数签名更改为至少接受一个参数。):

代码语言:javascript
复制
template<size_t N>
using cstring_literal_type = const char (&)[N];

template<size_t... Ns>
void foo(cstring_literal_type<Ns>... args)
{
    for (const char* arg : {args...})
    {
        // Real work
    }
}
票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50353535

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档