为什么传递给模板函数的std :: string对象不喜欢std :: string重载?

时间:2016-08-31 23:12:37

标签: c++ c++11

我正在编写一个程序,我希望能够干净利地将一个字符串用引号括起来,而不必像

那样做
std::string firstString = "This is a string";
std::string myString = "\"" + firstString + "\"";    

所以我编写了几个模板函数来获取它们的参数并将它们用引号括起来。我还包括我第一次(天真)尝试编写一个通用的toString()函数(我知道to_string,但我也是为了学习这个)。

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <typeinfo>

template <typename T>
std::string toString(const T &convert)
{
    std::string returnString{""};
    std::stringstream transfer;
    transfer << convert;
    transfer >> returnString;
    return returnString;
}

template<typename T>
std::string tQuoted(const T &convert)
{
    std::cout << "Called template overload" << std::endl;
    return ("\"" + toString(convert) + "\"");
}

template<typename T>
std::string tQuoted(const std::string &convert)
{
    std::cout << "Called std::string overload" << std::endl;
    return ("\"" + convert + "\"");
}

template<typename T>
std::string tQuoted(const char *convert)
{
    std::cout << "Called const char overload" << std::endl;
    return ("\"" + static_cast<std::string>(convert) + "\"");
}

template<typename T>
std::string tQuoted(std::string convert)
{
    std::cout << "Called normal std::string overload" << std::endl;
    return ("\"" + convert + "\"");
}

template<typename T>
std::string tQuoted(std::string&& convert)
{
    std::cout << "Called rvalue std::string overload" << std::endl;
    return ("\"" + convert + "\"");
}

int main()
{
    std::vector<std::string> my{"Hello", "30 Days Of Coding", "All Work And No Play"};

    std::string myString = "Hello, World!";
    std::string *strPtr = &myString;
    std::string *mySuperPtr = new std::string{"He's a cockaroach"};

    for (std::vector<std::string>::const_iterator iter = my.begin(); iter != my.end(); iter++) {
        std::cout << tQuoted(*iter) << std::endl;
    }

    std::cout << tQuoted(myString) << std::endl;
    std::cout << tQuoted(*strPtr) << std::endl;
    std::cout << tQuoted(mySuperPtr) << std::endl;
    std::cout << tQuoted(std::string{"Another string"}) << std::endl;

    delete mySuperPtr;
    mySuperPtr = nullptr;

    return 0;

}

其中每一个都调用模板构造函数:

Called template overload
"Hello"
Called template overload
"30"
Called template overload
"All"
Called template overload
"Hello,"
Called template overload
"Hello,"
Called template overload
"0x13cad10"
Called template overload
"Another"

当然,一个不那么天真的toString()方法会做基本的检查,看看参数是否是一个std :: string,如果是的话,只返回它。似乎std :: stringstream在遇到字符串中的第一个空格时会停止(因此截断输出)。但是,这不是我困惑的主要焦点。

对不起这个非常基本的问题,但是这个问题真让我难过。感谢您提供的任何帮助。

2 个答案:

答案 0 :(得分:6)

您没有正确专门化模板功能。这是如何正确地专门化它:

template<>
std::string tQuoted(const std::string &convert)
{
    std::cout << "Called std::string overload" << std::endl;
    return ("\"" + convert + "\"");
}

结果输出变为:

Called std::string overload
"Hello"
Called std::string overload
"30 Days Of Coding"
Called std::string overload
"All Work And No Play"
Called std::string overload
"Hello, World!"
Called std::string overload
"Hello, World!"
Called template overload
"0x1c27d10"
Called std::string overload
"Another string"

请注意

tQuoted(mySuperPtr) 

mySuperPtr是指向字符串的指针,而不是字符串,因此不会使用专门的模板函数。

答案 1 :(得分:3)

仔细查看您编写的这个函数模板:

template<typename T>
std::string tQuoted(const std::string &convert)
{
    std::cout << "Called std::string overload" << std::endl;
    return ("\"" + convert + "\"");
}

这是一个功能模板,模板参数为T,但T来自何处?它无处可去。具体来说,这是一个非推断的背景。编译器无法告知T来自tQuoted(myString)的内容,您必须明确调用tQuoted<X>(myString)

但在这种情况下,你实际上并不想要T。你不是要写一个新的功能模板。您正在尝试专门为std::string编写重载(正如函数正文所示)。所以只需写一个重载:

std::string tQuoted(const std::string &convert)
{
    std::cout << "Called std::string overload" << std::endl;
    return ("\"" + convert + "\"");
}

不需要template。所有其他重载都是如此 - 它们都是意外的函数模板,模板参数是非推导的上下文。

避免使用函数模板,除非你确实需要它们。在这种情况下,你没有。见Why Not Specialize Function Templates?