【问题标题】:How to invoke c_str() for std::string variadic template parameters?如何为 std::string 可变参数模板参数调用 c_str()?
【发布时间】:2020-07-28 23:06:46
【问题描述】:

我有一个接受格式字符串 + 参数的方法(就像 printf()),但是,我为此目的使用可变参数模板:

template<typename... Args>
static void log(const char* pszFmt, Args&&... args)
{
    doSomething(pszFmt, std::forward<Args>(args)...);
}

一些 args 可以是 std::string 实例。是否可以确保 doSomething 永远不会接受 std::string,但总是接受 const char* 而不是传递给 log() 的每个源 std::string

换句话说,我需要一种将所有args 转发到doSomething() 的方法,将所有std::string 参数替换为std::string::c_str() 返回的参数。

提前致谢!

【问题讨论】:

    标签: c++ templates stl variadic-templates


    【解决方案1】:

    这是另一种解决方案。如果您的记录器只是打印每个参数而不“存储”它,那么就不需要完美转发参数,一个简单的按引用传递就足够了。

    在这种情况下,您可以简单地为各种“可打印”类型重载或专门化打印机函数。

    template <class T>
    decltype(auto) printer(T const& t) {
        return t;
    }
    
    inline const char* printer(std::string const& t) {
        return t.c_str();
    }
    
    template<typename... Args>
    void log(const char* pszFmt, Args const&... args) {
        printf(pszFmt, printer(args)...);
    }
    
    int main() {
        std::string str{"xyz"};
        log("%s %s %s\n", "abc", std::string("def"), str);
    }
    

    注意:在重载解析期间,非模板重载将始终是首选。

    【讨论】:

    • 也是伟大而简约的解决方案。谢谢!
    【解决方案2】:

    您可以定义自己的“转发”方法:

    template<typename T>
    decltype(auto) myForward(T&& t)
    {
        return t;
    }
    
    template<>
    decltype(auto) myForward(std::string& t)
    {
        return t.c_str();
    }
    
    
    template<>
    decltype(auto) myForward(std::string&& t)
    {
        return t.c_str();
    }
    
    
    template<typename... Args>
    static void log(const char* pszFmt, Args&&... args)
    {
        doSomething(pszFmt, myForward<Args>(std::forward<Args>(args))...);
    }
    

    【讨论】:

    • 你仍然需要std::forward args 否则它们会变成左值引用。
    • 返回auto会按值返回。总是。最好返回T&amp;&amp;
    • 你们说的都对,我需要decltype(auto)std::forward,我编辑了我的答案
    • myForward(T&amp;&amp;) 需要两个重载,而不是 string 版本。将string 带到const&amp; 就足够了。
    【解决方案3】:

    C++17 版本

    您可以使用 SFINAE 来实现:

    #include <iostream>
    #include <utility>
    #include <string>
    
    template <typename, typename = void>
    struct has_c_str : std::false_type {};
    
    template <typename T>
    struct has_c_str<T, std::void_t<decltype(&T::c_str)>> : std::is_same<char const*, decltype(std::declval<T>().c_str())>
    {};
    
    template <typename StringType,
              typename std::enable_if<has_c_str<StringType>::value, StringType>::type* = nullptr>
    static void log(const char* pszFmt, StringType const& arg) {
        std::cout << "std::string version" << std::endl;
    }
    
    template <typename StringType,
              typename std::enable_if<!has_c_str<StringType>::value, StringType>::type* = nullptr>
    static void log(const char* pszFmt, StringType arg) {
        std::cout << "const char * version" << std::endl;
    }
    
    template <typename... Args>
    static void log(const char* pszFmt, Args&&... args) {
        log(pszFmt, std::forward<Args>(args)...);
    }
    
    int main() {
        log("str", std::string("aa")); // output: std::string version
        log("str", "aa");              // output: const char * version
        return 0;
    }
    

    完整演示here

    【讨论】:

    • 我想接受所有三个答案,因为它们每个都解决了任务并且有自己的优势。接受更简单的,因为它更适合我的需求,但是,SFINAE 应用程序也很棒。
    猜你喜欢
    • 1970-01-01
    • 2017-10-10
    • 2021-10-01
    • 2020-04-07
    • 2016-12-01
    • 1970-01-01
    • 2020-02-24
    • 2014-01-19
    • 1970-01-01
    相关资源
    最近更新 更多