如何为 std::string 可变模板参数调用 c_str()?

How to invoke c_str() for std::string variadic template parameters?

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

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

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

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

提前致谢!

您可以定义自己的 "forwarding" 方法:

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))...);
}

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

这是一个替代解决方案。如果您的记录器只是打印每个参数而不是 "store" 它,那么就没有必要完美转发参数,一个简单的按引用传递就足够了。

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

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);
}

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