首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >各种模板函数的函数模板专门化(printf)

各种模板函数的函数模板专门化(printf)
EN

Stack Overflow用户
提问于 2020-03-15 16:00:27
回答 2查看 141关注 0票数 1

我尝试改进一些Arduino C++11代码:尝试创建一个类似printf的函数,专门处理字符串,这样我就不必在使用它的地方调用c_str()。基本上,对于任何内置类型,如等,我只想以-is传递arg,对于字符串,pass返回c_str()。遇到了一些障碍,所以我在一些在线编译器中尝试了这一点。起点是这样的,使用std::string而不是String:

代码语言:javascript
复制
#include <string>

class SerialOut {
public:
    template<class ...Ts>
    static void error(const char* msg, Ts... args) {
        printf(msg, args...);
    }
};

int main() {
    std::string greeting("hi");
    SerialOut::error("Message %d %s\n", 1, greeting.c_str());
}

因此,我尝试创建一个函数模板,该模板只返回它获得的值,并对std::string进行专门化:

代码语言:javascript
复制
#include <string>

template <typename T, typename R=T> R raw(T& x) {return x;}
template <> const char* raw<>(std::string& x) {return x.c_str();}

class SerialOut {
public:
    template<class ...Ts>
    static void error(const char* msg, Ts... args) {
        printf(msg, raw(args)...);
    }
};

int main() {
    std::string greeting("hi");
    SerialOut::error("Message %d %s\n", 1, greeting);
}

当我在https://repl.it/languages/cpp11中运行这个程序时,会得到一个编译错误:

代码语言:javascript
复制
clang version 7.0.0-3~ubuntu0.18.04.1 (tags/RELEASE_700/final)
 clang++-7 -pthread -std=c++11 -o main main.cpp
main.cpp:10:25: error: cannot pass object of non-trivial type
      'std::__cxx11::basic_string<char>' through variadic function; call will abort at
      runtime [-Wnon-pod-varargs]
            printf(msg, raw(args)...);
                        ^
main.cpp:16:20: note: in instantiation of function template specialization
      'SerialOut::error<int, std::__cxx11::basic_string<char> >' requested here
        SerialOut::error("Message %d %s\n", 1, greeting);
                   ^
1 error generated.
compiler exit status 1

对于编译器,没有错误,但是没有选择raw()专门化,因此用于问候的输出是垃圾。

在Arduino IDE中,我得到了一个稍微不同的错误(当然,在用字符串替换std::string之后):

代码语言:javascript
复制
sketch\mqtt.cpp.o: In function `char const* raw<String, char const*>(String&)':

sketch/utils.h:15: multiple definition of `char const* raw<String, char const*>(String&)'

sketch\Thermistor.cpp.o:sketch/utils.h:15: first defined here

sketch\sketch.ino.cpp.o: In function `char const* raw<String, char const*>(String&)':

sketch/utils.h:15: multiple definition of `char const* raw<String, char const*>(String&)'

sketch\Thermistor.cpp.o:sketch/utils.h:15: first defined here

我尝试了几个关于raw()函数的变体,但都没有用。我想我只是缺少了一个微妙之处,或者在C++11中不可能做到这一点。

Update:我找到了变量宏:不能通过“.”传递非复制类型的对象。,其中一个答案在C++14中解决了上述问题(基本上使用decltype(auto)和重载而不是专门化)。我在它上添加了一个细微的变化,它在C++11中也能工作,与“内联”一样,它也可以在Arduino C++中工作(在重载中没有“内联”,上面关于多个定义的消息-原来这是一个链接器消息,所以它可以编译,我猜Arduino变体不会像其他编译器那样内联“明显内联”函数)。

EN

回答 2

Stack Overflow用户

发布于 2020-03-15 16:48:58

一些类似的东西,也许:

代码语言:javascript
复制
template <typename T>
struct SerialHelper {
    static T raw(T val) { return val; }
};

template <>
struct SerialHelper<std::string> {
    static const char* raw(const std::string& val) { return val.c_str(); }
};


class SerialOut {
public:
    template<class ...Ts>
    static void error(const char* msg, Ts... args) {
        printf(msg, SerialHelper<Ts>::raw(args)...);
    }
};

演示

票数 0
EN

Stack Overflow用户

发布于 2020-03-15 17:09:49

基于变量宏:不能通过“.”传递非复制类型的对象。,我让它处理这个非常简单的更改,它适用于C++11和Arduino C++:

代码语言:javascript
复制
#include <string>

template <typename T> T raw(const T& x) {return x;}
inline const char* raw(const String& x) {return x.c_str();}

class SerialOut {
public:
    template<class ...Ts>
    static void error(const char* msg, Ts... args) {
        printf(msg, raw(args)...);
    }
};

int main() {
    std::string greeting("hi");
    SerialOut::error("Message %d %s\n", 1, greeting);
}

感谢@IgorTandetnik的评论,原因很清楚。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60694807

复制
相关文章

相似问题

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